[这个blog需要恢复] 432D
感谢@nonamenotitle 的指教
题意:
给你个字符串
求所有的前缀=后缀的长度,以及这个前缀(后缀)在整个字符串里面出现了多少次
有一个神奇的东西叫做Z-box
d[i]表示从i开始,和前缀匹配了多少
例如AABAABAABAAB的d分别为
12 1 0 9 1 0 6 1 0 3 1 0
这题只要求出d就可以了
然后就是利用Z-box的做法,Z-box思路如下
每个d都能组成一段区间(d=0区间为空)
如果当前位置以前在所有区间中,右端点最右的区间
如果这个点在区间内,那么直接利用之前的值,也就是说它在区间内所对应的前面的区间的东西
例如还是刚才那个串
AABAABAABAAB
我们在考虑第11个位置的时候,发现一个匹配到结束的位置(10)是最右的
那么我们我们在的串AAB不管是前面还是后面,都是一样的
所以直接调用f(2)的值(1 2 3与10 11 12相对等价)
但是如果这个匹配超出了预期,就是说匹配到了区间的右端点
就暴力开始往右跑计算
反正随着右端点的推进,一共推进最多n次所以是O(n)的
字符串好神奇啊
代码如下:
#include<set>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<string>
#include<time.h>
#include<math.h>
#include<memory>
#include<vector>
#include<bitset>
#include<fstream>
#include<stdio.h>
#include<utility>
#include<sstream>
#include<string.h>
#include<iostream>
#include<stdlib.h>
#include<algorithm>
using namespace std;
char a[100005];
int d[100005];
int sum[100005];
int main()
{
#ifdef absi2011
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif
scanf("%s",a);
memset(sum,0,sizeof(sum));
int i;
int n=strlen(a);
int maxl=0,maxr=0;
d[0]=n;
for (i=1;i<n;i++)
{
int t=i-maxl;
if (i>=maxr)
{
d[i]=0;
for (;;)
{
if (a[i+d[i]]!=a[d[i]]) break;
d[i]++;
}
if (i+d[i]>maxr)
{
maxl=i;
maxr=i+d[i];
}
}
else
{
d[i]=d[t];
if (i+d[i]>=maxr) d[i]=maxr-i;
for (;;)
{
if (a[i+d[i]]!=a[d[i]]) break;
d[i]++;
}
if (i+d[i]>maxr)
{
maxl=i;
maxr=i+d[i];
}
}
//printf("%d ",d[i]);
}
int ans=0;
for (i=0;i<n;i++)
{
sum[d[i]]++;
if (d[n-i-1]>i)
{
ans++;
}
}
printf("%d\n",ans);
for (i=n;i>=0;i--)
{
sum[i]+=sum[i+1];
}
for (i=1;i<=n;i++)
{
if (d[n-i]>=i)
{
printf("%d %d\n",i,sum[i]);
}
}
return 0;
}
评论 (0)