AcWing 1052. 设计密码

原题链接

考察:有限状态机dp+kmp

鬼才想得到的解法

思路:

        在验证某串a是否为b的子串时,常常使用kmp算法.当子串匹配下标j = 子串长度时,说明串a是串b的子串.如果i取到串b的最后一位时,j还没有 = a长度,说明a不是b的子串.

        在这道题,我们需要构造串b,使得a不是b的子串,也就是j不能取到串a的长度.假设当前j是a的待匹配位,b的下一位构造位为i.第i位有26种取值,对于每个取值,j的下一步位置大多不同.将下一步的取值看成转化的状态的话,就能构造f[i][j]表示b构造了前i个字符,匹配了a子串的前j位.那么f[i][j] += f[i-1][k].

        因为第i个字符是需要枚举a~z的,取到a[k]后要做一次kmp匹配,求第i位可以匹配的位置.

 1 #include <iostream>
 2 #include <cstring>
 3 using namespace std;
 4 const int N = 55,Mod = 1e9+7;
 5 char s[N];//f[i][j]表示构造前i个字符匹配前j个字符的方法数
 6 int n,ne[N],len,f[N][N];
 7 void init()
 8 {
 9     for(int i=0,j=2;j<=len;j++)
10     {
11         while(i&&s[i+1]!=s[j]) i = ne[i];
12         if(s[i+1]==s[j]) i++;
13         ne[j] = i;
14     }
15 }
16 int main()
17 {
18     scanf("%d%s",&n,s+1);
19     len = strlen(s+1);
20     init();
21     f[0][0] = 1;
22     for(int i=1;i<=n;i++)//已经构造了i个字符.
23       for(int j=0;j<len;j++)//j是已经子串匹配了的位置
24         for(char k=a;k<=z;k++)//枚举密码的下一个字符是k.
25         {
26             int t = j;
27             while(t&&s[t+1]!=k) t = ne[t];
28             if(s[t+1]==k) t++;
29             f[i][t] += f[i-1][j];//构造了密码前i个字符,最后一个字符与子串匹配了j个
30             f[i][t]%=Mod;
31         }
32     int res = 0;
33     for(int i=0;i<len;i++) res+=f[n][i],res%=Mod;
34     printf("%d\n",res);
35     return 0;
36 }

 

AcWing 1052. 设计密码

上一篇:2A - Winner


下一篇:windows 桌面应用UI自动化测试工具