Description
某日mhy12345在教同学们写helloworld,要求同学们用程序输出一个给定长度的字符串,然而发现有些人输出了一些“危险”的东西,所以mhy12345想知道对于任意长度 n n 的小写字母字符串,不包含危险串的字符串个数
Input
多组数据,以 EOF EOF 结束。对于每组数据,第一行一个数 n n,表示字符串的长度,第二行一个字符串 str str 表示危险串。
Output
对于每组数据,输出一个整数表示答案 mod(109+7)mod(109+7)的值。
Sample Input
5
a
Sample Output
9765625
Data Constraint
对于10%10%的数据,|str|=1|str|=1
对于另30%30%的数据,n≤5n≤5
对于另30%30%的数据,危险串不存在相同字符
对于100%100%的数据,0≤|str|≤100,0≤n≤100000≤|str|≤100,0≤n≤10000
Solution
考试时想过用容斥,但好像不可做……所以就换了一种方式——DP
设状态 f[i,j] f[i,j] 为当前已经做到第i位,已匹配了前j位危险串的合法方案数。
那么对于不存在相同字符的串就有如下转移:
当下一位选 S[j+1] S[j+1] 时,转移到f[i,j+1]f[i,j+1]。
当下一位选 S[1] S[1] 时,转移到 f[i,1] f[i,1] 。
选其它,转移到 f[i,0] f[i,0]
对于存在相同字符的串,那就稍微有些不同了。
。由于给定串可能包含重复字母,因此
下一位不选 s[j+1] s[j+1] 时,不一定转移到 f[i,0] f[i,0] ,而是可能转移到
f[i][next[j]+1]f[i][next[j]+1]、f[i][next[next[j]]+1]f[i][next[next[j]]+1]、f[i][next[next[next[j]]]+1]f[i][next[next[next[j]]]+1]……其中
next 数组是 kmp 的 next 数组,具体转移到哪个要
看下一位具体选什么。
所以我们预处理一个p[j,k]数组,表示选j字符最多可以匹配到第几位。反之如果不匹配到最多的那一位就有可能提前匹配了一个危险串,就会有不合法的可能。
所以就有 f[i+1,p[j,k]]+=f[i,j] f[i+1,p[j,k]]+=f[i,j]
Code
一个 90 90 分代码,求找bug。
const mo=1000000007;
var m,i,j,k:longint;
n,ans:int64;
c:array[1..26] of char;
p:array[0..105,1..26] of longint;
next:array[1..105] of longint;
f:array[0..10005,0..105] of int64;
s:string;
function min(x,y:longint):longint;
begin
if x<y then exit(x);exit(y);
end;
begin
for i:=1 to 26 do c[i]:=chr(ord('a')+i-1);
while not eof do begin
readln(n);
readln(s);
m:=length(s);ans:=0;
fillchar(next,sizeof(next),0);
fillchar(p,sizeof(p),0);
fillchar(f,sizeof(f),0);
for i:=2 to m do begin
while (j<>0) and (s[i]<>s[j+1]) do j:=next[j];
if s[i]=s[j+1] then inc(j);
next[i]:=j;
end;
for i:=0 to m-1 do begin
for j:=1 to 26 do begin
k:=i;
while (k<>0) and (c[j]<>s[k+1]) do k:=next[k];
if c[j]=s[k+1] then k:=k+1;
p[i,j]:=k;
end;
end;
for i:=0 to n-1 do begin
for j:=0 to min(m-1,i) do begin
for k:=1 to 26 do begin
f[i+1,p[j,k]]:=(f[i+1,p[j,k]]+f[i,j]) mod mo;
end;
end;
end;
for i:=0 to m-1 do ans:=(ans+f[n,i]) mod mo;
writeln(ans);
end;
end.