传送门
这题意不是一般人能读懂的,为了读懂题目,我还特意去翻了题解[手动笑哭]
题目大意:
给定一个字符串s
对于s的每一个前缀子串s1,规定一个字符串Q,Q满足:Q是s1的前缀子串且Q不等于s1且s1是字符串Q+Q的前缀.设siz为所有满足条件的Q中Q的最大长度(注意这里仅仅针对s1而不是s,即一个siz的值对应一个s1)
求出所有siz的和
不要被这句话误导了:
求给定字符串所有前缀的最大周期长度之和
正确断句:求给定字符串 所有/前缀的最大周期长度/之和
我就想了半天:既然是"最大周期长度",那不是唯一的吗?为什么还要求和呢?
其实这题要AC并不难(看通过率就知道)
看图
要满足Q是s1的前缀,则Q的15位和s1的15位是一样的,又因为s1是Q+Q的前缀,所以又要满足s1的68位和Q+Q的68位一样,即s1的68位和Q的13位相等,回到s1,标蓝色的两个位置相等.
回顾下KMP中next数组的定义:next[i]
表示对于某个字符串a,"a中长度为next[i]
的前缀子串"与"a中以第i为结尾,长度为next[i]
的非前缀子串"相等,且next[i]
取最大值
是不是悟到了什么,是不是感觉这题和next数组冥冥之中有某种相似之处?
但是,这仅仅只是开始
按照题目的意思,我们要让Q的长度最大,也就是图中蓝色部分长度最小,但是next中存的是蓝色部分的最大值,显然,两者相违背,难道我们要改造next数组吗?明显不行,若next存储的改为最小值,则原来求next的方法行不通.考虑换一种思路(一定要对KMP中next的求法理解透彻,不然下面看不懂,不行的复习一下),我们知道对于next[i],next[next[i-1]],next[next[next[i]]]...
都能满足"前缀等于以i结尾的子串"这个条件,且越往后,值越小,所以,我们的目标就定在上面序列中从后往前第一个不为0的next值
极端条件下,暴力跑可以去到O(n^2),理论上会超时(我没试过)
两种优化:
j=next[j]
这一语句称作"j跳了一次"next[i][k]
表示结尾为i,j跳了2^k的前缀字符长度(也就是next[i][0]
等价于原来的next[i]
) int tmp = i;
for(rr int j = siz[i] ; j >= 0 ; --j)//siz[i]是next[i][j]中第一个为0的小标j,注意倒序枚举
if(next[tmp][j] != 0)//如果不为0则跳
tmp = next[tmp][j];
倍增方法在字符串长度去到106时是非常危险的,带个log理论是2*107左右,常数再大那么一丢丢就TLE了,还好数据比较水,但是作为倍增和KMP的练习做一下也是不错的
最后,记得开longlong(不然我就一次AC了)
#include
#include
#include
#define nn 1000010
#define rr register
#define ll long long
using namespace std;
int next[nn][30] ;
int siz[nn];
char s[nn];
int n;
int main() {
// freopen("P3435_3.in" , "r" , stdin);
cin >> n;
do
s[1] = getchar();
while(s[1] < 'a' || s[1] > 'z');
for(rr int i = 2 ; i <= n ; i++)
s[i] = getchar();
next[1][0] = 0;
for(rr int i = 2 , j = 0 ; i <= n ; i++) {
while(j != 0 && s[i] != s[j + 1])
j = next[j][0];
if(s[j + 1] == s[i])
++j;
next[i][0] = j;
}
rr int k = log(n) / log(2) + 1;
for(rr int j = 1 ; j <= k ; j++)
for(rr int i = 1 ; i <= n ; i++) {
next[i][j] = next[next[i][j - 1]][j - 1];
if(next[i][j] == 0)
siz[i] = j;
}
ll ans = 0;
for(rr int i = 1 ; i <= n ; ++i) {
int tmp = i;
for(rr int j = siz[i] ; j >= 0 ; --j)
if(next[tmp][j] != 0)
tmp = next[tmp][j];
if(2 * (i - tmp) >= i && tmp != i)
ans += (ll)i - tmp;
}
cout << ans;
return 0;
}