题目地址:http://codeforces.com/contest/535/problem/D
题意:题目理解了好久,简化后的大意是,给定原字符串的长度N,子串匹配次数,该子串s,以及可匹配的位置y1,y2…yi,最后要求输出原字符串的所有可能的情况数之和,如果不存在就打印0
刚拿到这题时,没用kmp,单纯就对题意进行模拟,对字符串的每个字符定义一个状态数组,有字符为1,没有为0,每次匹配不断更新数组,N进行减的操作,最后表示空位的个数,答案即为Qpow(26,N,1e9+7),t了三发(=.=),看了网上别的博主写的题解orz,思路清晰多了,现在提供题解和分析
对所给的子串进行求next数组操作(敲板子),重点是e[M]数组,e[i]表示重叠i个时的匹配与否,匹配为1,否为0,注意该循环从M开始逆向进行利用next数组赋值(M==0要特判)
for(int p=m;p;p=nxt[p])
{
e[p]=1;
}
next数组和e数组赋值结束后,题目就结束一大半了,在ans函数只需要对每两个匹配点求个距离dis,如果大于M,则无需判断重叠,直接N-M,小于等于则要判断,匹配就减dis,不匹配return0(我的pos是从0到M-1,pos[M]要加个等于N+1,方便逆序循环求第一个dis)
完整代码如下:
#include<iostream>
using namespace std;
#define ll long long
#define mod 1000000007
const int siz=1000005;
bool e[siz];
int N,M,m,i;
char s[siz];
int pos[siz];
int nxt[siz];
ll Qpow(ll a,ll b,ll p)
{
ll ans=1;
while(b>0)
{
if(b&1) ans=(ans*a)%p;
a=(a*a)%p;
b>>=1;
}
return ans%p;
}
void getnext()
{
int j=0;nxt[1]=0;
for(i=2;i<=m;++i)
{
while(j&&s[j+1]!=s[i])j=nxt[j];
if(s[j+1]==s[i])++j;
nxt[i]=j;
}
for(int p=m;p;p=nxt[p])
{
e[p]=1;
}
}
int ans()
{
int laplen,dis;
for(i=M-1;i>=0;--i)
{
dis=pos[i+1]-pos[i];
if(dis>=m)
N-=m;
else
{
laplen=m-dis;
if(e[laplen]==0)
return 0;
else N-=dis;
}
}
return Qpow(26,N,mod);
}
int main()
{
int i;
cin>>N>>M;
scanf("%s",s+1);
m=strlen(s+1);
for(i=0;i<M;i++)
{
cin>>pos[i];
}
pos[M]=N+1;
if(M==0)
cout<<Qpow(26,N,mod)<<endl;
else
{
getnext();
cout<<ans()<<endl;
}
}
第一次写题解博客有些叨2333,大佬路过勿忘指点= =