POI 2012 OKR - A Horrible Poem 题解

2024-02-12 09:40
文章标签 2012 题解 okr poi poem horrible

本文主要是介绍POI 2012 OKR - A Horrible Poem 题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目传送门

题目大意: 有一个长度为 n n n的字符串,每次询问一个字串的最短循环节。

题解

最最暴力的做法:枚举长度 l e n len len,每次将该长度的字串与原串暴力匹配一遍。

然后考虑优化:

1.

显然 l e n len len 一定是 n n n 的因数,于是枚举 l e n len len 的时候只需要枚举 n n n 的因数即可。

2.

匹配可以用哈希来搞,但是如果将相邻的两节依次匹配的话还是很慢,最坏时间复杂度为 O ( n ) O(n) O(n),于是如何进一步优化呢?

仔细观察一下匹配过程:
在这里插入图片描述

每一次匹配都是跟自己右边的匹配,然后我们又知道,hash的匹配无论多长都是 O ( 1 ) O(1) O(1) 的,于是像这样子搞一搞——
在这里插入图片描述

将字符串复制一份,一个掐头,一个去尾,然后直接匹配,效果与两两匹配是一样的,因为现在上下对应的两个就是原来左右相邻的两个。

3.

part 1

经过尝试后,发现用 O ( n ) O(\sqrt n) O(n ) 的方法来枚举因数还是不够优秀的(卡了半天常也过不去,可能是我太蒟了qaq),于是发掘一下更加优秀的做法。

一个数可以写成 a 1 b 1 a 2 b 2 . . . a_1^{b_1}a_2^{b_2}... a1b1a2b2... 的形式,对于每个数,虽然因数可能很多,但是它的质因数个数实际少得可怜,并且每一个数的 a a a 数组和 b b b 数组都是可以预处理的,于是只需要枚举每一个质因数的指数就可以了。

但这种做法只是比 O ( n ) O(\sqrt n) O(n ) 的做法快那么一点,所以总的时间复杂度依然不乐观……

综上,枚举因数似乎不大好做,于是需要换一种做法。


part 2

有一个看似很废的性质:假如长度 x x x 是字符串的一个循环节,那么当 x ∗ k x*k xk 也是总长的一个因数时, x ∗ k x*k xk 也一定是一个循环节。

一开始觉得它很废是因为如果要得到 x ∗ k x*k xk 首先肯定要得到 x x x,但那我都有 x x x 了我还要 x ∗ k x*k xk 有鸟用?

但是换一个角度看这条性质,它其实等价于——假如长度 x x x 不是循环节,那么它的因数就都不是循环节。

于是根据这条性质,就得到了另一种做法,将 l e n len len 的质因子都找出来,假如 l e n / len/ len/某质因子 之后,得到的长度依然是一个循环节,那么 l e n len len 就除以这个因子,否则就不除,因为除了之后得到的那个因数不是循环节,根据上述性质,我们并不能用这个奇怪的因数得到答案。

4.

卡常qwq

最后附上代码,还有些没讲到的细节里面有:

#pragma GCC optimize(3)
#include <cstdio>
#include <cstring>
#define ull unsigned long long
#define jinzhi 315ull
#define mod 2147463647ull
#define R register
#define maxn 500010int n,m;
char s[maxn];
inline int ch(char x){return x-'a';};
ull hash[maxn],power[maxn];
inline ull get(int l,int r){return (hash[r]-hash[l-1]*power[r-l+2]%mod+mod)%mod;};
//滚动哈希求子串的哈希值
inline bool check(const int x,const int y,int blo){return get(x,y-blo)==get(x+blo,y);}
//看长度blo是不是x~y这个子串的循环节
inline char nc()
{static char buf[1000010],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}//读优
void read(int &x)
{x=0;char ch=nc();while(ch<'0'||ch>'9')ch=nc();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=nc();
}
bool v[maxn];
int prime[maxn],t=0;
void work()//线性筛素数
{for(int i=2;i<=maxn-10;i++){if(!v[i])prime[++t]=i;for(int j=1;j<=t;j++){if(prime[j]*i>maxn-10)break;v[prime[j]*i]=true;if(i%prime[j]==0)break;}}
}
int minfactor[maxn];
//每一个数的最小质因子,后面分解质因数时用到
void work2()//预处理minfactor
{for(int i=1;i<=t;i++){for(int j=1;j*prime[i]<=maxn-10;j++)if(!minfactor[prime[i]*j])minfactor[prime[i]*j]=prime[i];}
}
int a[maxn],st;int main()
{scanf("%d",&n);scanf("%s\n",s+1);work();work2();for(R int i=1;i<=n;++i)hash[i]=(hash[i-1]*jinzhi%mod+(ull)s[i])%mod;power[1]=1;for(R int i=2;i<=n;++i)power[i]=power[i-1]*jinzhi%mod;read(m);R int x,y;while(m--){read(x);read(y);int block(y-x+1);st=0;while(block!=1)a[++st]=minfactor[block],block/=minfactor[block];//用a存下将block分解质因数后的结果block=y-x+1;//记得还原blockfor(int i=1;i<=st;i++)if(check(x,y,block/a[i]))block/=a[i];printf("%d\n",block);}
}

这篇关于POI 2012 OKR - A Horrible Poem 题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/702297

相关文章

SpringBoot使用Apache POI库读取Excel文件的操作详解

《SpringBoot使用ApachePOI库读取Excel文件的操作详解》在日常开发中,我们经常需要处理Excel文件中的数据,无论是从数据库导入数据、处理数据报表,还是批量生成数据,都可能会遇到... 目录项目背景依赖导入读取Excel模板的实现代码实现代码解析ExcelDemoInfoDTO 数据传输

java poi实现Excel多级表头导出方式(多级表头,复杂表头)

《javapoi实现Excel多级表头导出方式(多级表头,复杂表头)》文章介绍了使用javapoi库实现Excel多级表头导出的方法,通过主代码、合并单元格、设置表头单元格宽度、填充数据、web下载... 目录Java poi实现Excel多级表头导出(多级表头,复杂表头)上代码1.主代码2.合并单元格3.

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

C - Word Ladder题解

C - Word Ladder 题解 解题思路: 先输入两个字符串S 和t 然后在S和T中寻找有多少个字符不同的个数(也就是需要变换多少次) 开始替换时: tips: 字符串下标以0开始 我们定义两个变量a和b,用于记录当前遍历到的字符 首先是判断:如果这时a已经==b了,那么就跳过,不用管; 如果a大于b的话:那么我们就让s中的第i项替换成b,接着就直接输出S就行了。 这样

【秋招笔试】9.07米哈游秋招改编题-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收集 100+ 套笔试题,笔试真题 会在第一时间跟新 🍄 题面描述等均已改编,如果和你笔试题看到的题面描述

LeetCode 第414场周赛个人题解

目录 Q1. 将日期转换为二进制表示 原题链接 思路分析 AC代码 Q2. 范围内整数的最大得分 原题链接 思路分析 AC代码 Q3. 到达数组末尾的最大得分 原题链接 思路分析 AC代码 Q4. 吃掉所有兵需要的最多移动次数 原题链接 思路分析 AC代码 Q1. 将日期转换为二进制表示 原题链接 Q1. 将日期转换为二进制表示 思路分析

最新OpenStreetMap POI数据(附下载教程)

OSM(OpenStreetMap)POI(Point of Interest)数据是指在OpenStreetMap上标记的各种兴趣点,如餐馆、酒店、公交站、学校等地点。这些数据在地理信息系统(GIS)应用中非常有用,可以帮助进行地图绘制、路径规划以及其他地理分析任务。 这里直接放出下载地址,有需要的可以自行下载,tips:国外城市的数据源质量比国内的要高一些; OpenStreetMap P

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

P2858 [USACO06FEB] Treats for the Cows G/S 题解

P2858 题意 给一个数组。每天把最左或者最右的东西卖掉,第 i i i个东西,第 d a y day day天卖出的价格是 a [ i ] ∗ d a y a[i]*day a[i]∗day。 记忆化搜索 void dfs(int l,int r,int day,ll sum){if(v[l][r]>=sum)return;v[l][r]=sum;if(l>r)//这就是dp答案{