参考博客:

BZOJ 3620: 似乎在梦中见过的样子


【KMP】似乎在梦中见过的样子

题目描述

「Madoka,不要相信QB!」伴随着Homura的失望地喊叫,Madoka与QB签订了契约。

这是Modoka的一个噩梦,也同时是上个轮回中所发生的事。为了使这一次Madoka不再与QB签订契约,Homura决定在刚到学校的第一天就解决QB。然而,QB也是有许多替身的(但在第八话中的剧情显示它也有可能是无限重生的),不过,意志坚定的Homura是不会放弃的——她决定消灭所有可能是QB的东西。现在,她已感受到附近的状态,并且把它转化为一个长度为n的字符串交给了学OI的你。

现在你从她的话中知道,所有形似于A+B+A的字串都是QB或它的替身,且∣A∣≥k,∣B∣≥1(位置不同其他性质相同的子串算不同子串,位置相同但拆分不同的子串算同一子串),然后你必须尽快告诉Homura这个答案——QB以及它的替身的数量。

注:对于一个字符串S,|S|表示S的长度。

输入

第一行一个字符串S,第二行一个数k。

输出

仅一行一个数ans,表示QB以及它的替身的数量。

样例输入

aaaaa
1

样例输出

6

提示

对于全部数据,1≤∣S∣≤1.5×104,1≤k≤100,且字符集为所有小写字母。


【题解】

就在参考博客里面了。

主要是自己不要意思把别人画的图复制过来。

枚举所有左端点然后进行向右看看有没有一个最长前缀是符合要求的。

就是这么一个想法,但是细节稍微有点多。因为两层for需要更细心。

【代码】:

 #include<cstdio>
#include<cstring>
#include<algorithm> using namespace std;
const int N = 1e5+; char s[N];
int k,n,ans;
int Next[N];
int main()
{
scanf("%s%d",s+,&k);
n = strlen( s+ ); //printf("%s %d %d\n",s+1,k,n);
//枚举所有合法的左端点,左端点 预留2*k的距离
for(int L=; L<= n-(k*) ; L++ ) { for(int i = ; i <= L ; i++ ) Next[i] = L- ; //提前预处理好Next数组
for(int i=L+,j=L-;i<=n;i++){
while( j != L- && s[i] != s[j+] ) j=Next[j];
if( s[i] == s[j+] ) j++ ;
Next[i] = j ;
} for(int i=L+,j=L-;i<=n;i++){
while( j != L- && s[i] != s[j+] ) j=Next[j];
if( s[i] == s[j+] ) j++ ; //预处理的Next派上用场了
//当最长前缀的两倍 > 当前串(右端点-左端点)的长度
//利用Next数组缩短距离
while( (j-L+)* >= (i-L+) ) j = Next[j] ; //符合题意 累加答案,即前缀长度大于k
if( j-L+ >= k ) ans ++ ;
} }
printf("%d\n",ans);
return ;
}
04-17 16:26