有一个由字符1,2,3,4组成的字符串s。给定一个字符串t,t一定是3s的一个长度大于等于|2s|的真前缀,求t后面的8个字符。
枚举s的长度,如果2|s|<=|t|<3|s|,并且第0个后缀和第s-1个后缀的lcp是|t|-|s|,那么说明这个s是靠谱的。可以用SA或者明天要学的Z函数来求。
想想怎么用前缀函数来检查:
- |s|和|t|的大小关系满足。
- 前2|s|个字符的周期是|s|的因数
- t[0..∣t∣−2∣s∣−1]=t[2∣s∣..∣t∣−1]t[0..|t|-2|s|-1]=t[2|s|..|t|-1]t[0..∣t∣−2∣s∣−1]=t[2∣s∣..∣t∣−1]
前两点都好检查,至于第三点,显然最后剩下的后缀是t的一个候选border,可以从j=pi[|t|-1]开始不断让j=pi[j-1],观察是否有一个等于|t|-2|s|的时候。这个做法感觉比较慢,先写写试试。
AC了,这道题目放在kmp这里主要就是考察如何求候选border。学会扩展kmp后再来看看。
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 2048, MOD = 1000000007;
char save[M];
int fail[M];
void get_fail(char *save)
{
fail[0] = 0;
for(int i=1; save[i]; ++i)
{
int j = fail[i-1];
while(j && save[i]!=save[j]) j=fail[j-1];
if(save[i]==save[j]) ++j;
fail[i] = j;
}
}
int cal_period(int pos)
{
if(pos % (pos - fail[pos-1]))
return pos;
return pos-fail[pos-1];
}
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int T = read();
while(T--)
{
scanf("%s",save);
int tn = strlen(save), sn;
get_fail(save);
for(sn=1; sn<=tn; ++sn)
{
if(tn<2*sn || tn>=3*sn) continue;
if(sn % cal_period(sn*2)) continue; //如果2s的周期不是sn的因数,显然不行
int suf = tn - sn*2, j = fail[tn-1];
while(j && suf != j)
j = fail[j-1];
if(suf==j) break; //找到了
}
for(int i=tn; i<tn+8; ++i)
putchar(save[i%sn]);
printf("...\n");
}
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}