DP × KMP

几道用到KMP的DP题:

hdu 5763    hdu 3689    hdu 3336    codeforces 494B    codevs 3945

关于KMP的nx数组:

如果在本文中看见了nx[]指的是所谓“成功指针”,或者getnx()函数跟本人之前的板子写的不一样......

都是因为求nx数组有两种方法!!!

详情请阅本人的另一篇文章:关于KMP算法的重大发现

好了我们进入正题~

一道一道来~

hdu 5763  Another Meaning

题意及样例:原题链接

设第一个串为A,长为n;第二个串为B,长为L

从1到n计算1~k能代表的意思的数量f[k]

如果A[k-L+1,k]==B

则f[k]=f[k-L]+f[k-1]

否则f[k]=f[k-1]

判断A[k-L+1,k]是否与B匹配就要靠KMP了

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define mod 1000000007
 5 using namespace std;
 6 
 7 int t,al,bl;
 8 char a[100005];
 9 char b[100005];
10 int nx[100005];
11 int fl[100005];
12 int f[100005]; 
13 
14 void getnx()
15 {
16     nx[1]=0;
17     for(int i=2,j=1;i<=bl;)
18     {
19         nx[i]=j;
20         while(j&&b[j]!=b[i])j=nx[j];
21         j++,i++;
22     }
23 }
24 
25 void kmp()
26 {
27     for(int i=1,j=1;i<=al;)
28     {
29         while(j&&b[j]!=a[i])j=nx[j];
30         if(j==bl)
31         {
32             fl[i]=1;
33             j=nx[j];
34         }
35         else j++,i++;
36     }
37 }
38 
39 int main()
40 {
41     scanf("%d",&t);
42     for(int cs=1;cs<=t;cs++)
43     {
44         memset(a,0,sizeof(a));
45         memset(b,0,sizeof(b));
46         memset(fl,0,sizeof(fl));
47         scanf("%s",a+1);
48         scanf("%s",b+1);
49         al=strlen(a+1);
50         bl=strlen(b+1);
51         getnx();
52         kmp();
53         f[0]=1;
54         for(int i=1;i<=al;i++)
55         {
56             if(fl[i])f[i]=(f[i-1]+f[i-bl])%mod;
57             else f[i]=f[i-1];
58         }
59         printf("Case #%d: %d\n",cs,f[al]);
60     }
61     return 0;
62 }
hdu 5763 Another Meaning

 

hdu 3689  Infinite monkey theorem

题目传送门

概率DP,不一定一出错就要重头开始打。用nx[ ]数组转移,避免字符的浪费。

这里的nx[ ]数组指的是成功指针......(玄学)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 int n,m,len;
 7 double p[30];
 8 char s[15];
 9 char key[30];
10 int nx[15];
11 double f[1005][15];
12 
13 void getnx()
14 {
15     nx[1]=0;
16     for(int i=2,j=0;i<=len;i++)
17     {
18         while(j&&s[i]!=s[j+1])j=nx[j];
19         if(s[j+1]==s[i])j++;
20         nx[i]=j;
21     }
22 }
23 
24 int main()
25 {
26     while(true)
27     {
28         memset(nx,0,sizeof(nx));
29         for(int i=1;i<=26;i++)p[i]=0.00;
30         for(int i=0;i<=1001;i++)
31         {
32             for(int j=0;j<=11;j++)
33             {
34                 f[i][j]=0.00;
35             }
36         }
37         scanf("%d%d",&n,&m);
38         if(!n)return 0;
39         for(int i=1;i<=n;i++)
40         {
41             char c[5];
42             scanf("%s",c+1);
43             key[i]=c[1];
44             scanf("%lf",&p[i]);
45         }
46         scanf("%s",s+1);
47         len=strlen(s+1);
48         getnx();
49         f[0][0]=1.00;
50         for(int i=1;i<=m;i++)
51         {
52             for(int j=0;j<len;j++)
53             {
54                 for(int k=1;k<=n;k++)
55                 {
56                     int ps=j;
57                     while(ps&&s[ps+1]!=key[k])ps=nx[ps];
58                     if(s[ps+1]==key[k])ps++;
59                     f[i][ps]+=f[i-1][j]*p[k];
60                 }
61             }
62         }
63         double ans=0.00;
64         for(int i=1;i<=m;i++)ans+=f[i][len];
65         printf("%.2lf%%\n",ans*100.00);
66     }
67 }
hdu 3689 Infinite monkey theorem

 

hdu 3336  Count the string

题目传送门

利用KMP中nx[ ]数组的性质。

上代码。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 int t,n,len,f[200005],ans;
 6 char s[200005];
 7 int nx[200005];
 8 
 9 void getnx()
10 {
11     for(int i=1;i<n;i++)
12     {
13         int j=nx[i];
14         while(j&&s[i]!=s[j]) j=nx[j];
15         nx[i+1]= s[i]==s[j] ? j+1:0;
16     }
17 }
18 int main()
19 {
20     scanf("%d",&t);
21     while(t--)
22     {
23         scanf("%d",&n);
24         scanf("%s",s);
25         len=strlen(s);
26         getnx();
27         memset(f,0,sizeof(f));
28         ans=0;
29         for(int i=1;i<=n;i++)
30         {
31             f[i]=f[nx[i]]+1;
32             ans+=f[i];
33             f[i]%=10007;
34             ans%=10007;
35         }
36         printf("%d\n",ans);
37     }
38 }
hdu 3336 Count the string

 

CodeForces 494B Obsessive String

题目传送门

比较复杂......

记录f[ ],f[ ]的前缀和s[ ],还有s[ ]的前缀和ss[ ] 。

KMP用来匹配字符串。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define mod 1000000007
 5 using namespace std;
 6 
 7 char a[100005],b[100005];
 8 int al,bl;
 9 int nx[100005];
10 int fl[100005];
11 int f[100005],s[100005],ss[100005];
12 
13 void getnx()
14 {
15     nx[1]=0;
16     for(int i=2,j=1;i<=bl;)
17     {
18         nx[i]=j;
19         while(j&&b[j]!=b[i])j=nx[j];
20         i++,j++;
21     }
22 }
23 
24 void kmp()
25 {
26     for(int i=1,j=1;i<=al;)
27     {
28         while(j&&a[i]!=b[j])j=nx[j];
29         if(j==bl)fl[i]=1,j=nx[j];
30         else i++,j++;
31     }
32 }
33 
34 int main()
35 {
36     scanf("%s",a+1);
37     al=strlen(a+1);
38     scanf("%s",b+1);
39     bl=strlen(b+1);
40     getnx();
41     kmp();
42     for(int i=1;i<=al;i++)
43     {
44         if(fl[i])f[i]=(i-bl+1+ss[i-bl])%mod;
45         else f[i]=f[i-1];
46         s[i]=(s[i-1]+f[i])%mod;
47         ss[i]=(ss[i-1]+s[i])%mod;
48     }
49     int ans=0;
50     for(int i=1;i<=al;i++)ans=(ans+f[i])%mod;
51     printf("%d",ans);
52 }
CodeForces 494B Obsessive String

 

CODEVS 3945 完美拓印

题目传送门

当成字符串匹配......

坑挺多的,各种方向都得匹配一次求出答案。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 int a[1000005],b[1000005],s1[1000005],s2[1000005],nx[1000005];
 7 int n,m,ans;
 8 
 9 void getnx()
10 {
11     nx[0]=nx[1]=0;
12     for(int i=1;i<n;i++)
13     {
14         int j=nx[i];
15         while(j&&s1[i]!=s1[j])j=nx[j];
16         nx[i+1]=(s1[i]==s1[j]?j+1:0);
17     }
18 }
19 
20 int kmp()
21 {
22     getnx();
23     int j=0,cnt=0;
24     for(int i=0;i<m;i++)
25     {
26         while(j&&s2[i]!=s1[j])j=nx[j];
27         if(s2[i]==s1[j])j++;
28         if(j==n)cnt++,j=nx[j];
29     }
30     return cnt;
31 }
32 
33 int main()
34 {
35     scanf("%d%d",&n,&m);
36     for(int i=0;i<n;i++)scanf("%d",&a[i]);
37     for(int i=0;i<m;i++)scanf("%d",&b[i]);
38     if(n==1)
39     {
40         printf("%d\n",m<<2);
41         return 0;
42     }
43     n--; m--;
44     for(int i=0;i<n;i++)s1[i]=a[i+1]-a[i];
45     for(int i=0;i<m;i++)s2[i]=b[i+1]-b[i]; 
46     ans+=kmp();
47     for(int i=0;i<n;i++)s1[i]=a[n-i]-a[n-i-1];
48     ans+=kmp();
49     for(int i=0;i<n;i++)s1[i]=0;
50     ans+=2*kmp();
51     printf("%d",ans);
52     return 0;
53 }
CODEVS 3945 完美拓印

结束了。

最后顺便说,有些字符串匹配的题可以用hash做。

这样可以偷懒,不用写KMP

好吧还是乖乖背KMP板子吧。

溜了。

转载于:https://www.cnblogs.com/eternhope/p/9480259.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值