NOIP2015提高组Day2 子串
2015-12-06 10:26
260 查看
题目
分析
我在考场上就想到了dp,但是。。算了,没有但是,这就是结果,是自己努力不够的后果。
好,回到正题。
方法0
:(考场的sb方法…..)设f[i,j,k]为a刚好匹配到i,b刚好匹配到j,分k组的方案数f[i,j,k]为a刚好匹配到i,b刚好匹配到j,分k组的方案数
转移十分暴力:a串枚举x,b串枚举y,再判断,然后累加。。
方法1:(by Philipsweng)
设fk,i,j表示a串最后分开后的最后子串[l,r]<=i,b串匹配到j,且分了k段的方案数fk,i,j表示a串最后分开后的最后子串[l,r]<=i,b串匹配到j,且分了k段的方案数Fk,i,j=Fk,i−1,j+∑ix=1,A[x..i]=B[j−i+x..j]Fk−1,x−1,j−i+x−1Fk,i,j = Fk,i−1,j +\sum_{x=1,A[x..i]=B[j−i+x..j]}^iFk−1,x−1,j−i+x−1
但是这样太慢了,我们进一步想想优化:
我们发现,若每次这样更新,就没有很好的利用到前面可用的状态。
故,我们接着分析一下fk−1,i,j和fk,i,jfk-1,i,j和fk,i,j
Fk,i,j=Fk,i−1,j+∑ix=1,A[x..i]=B[j−i+x..j]Fk−1,x−1,j−i+x−1Fk,i,j = Fk,i−1,j +\sum_{x=1,A[x..i]=B[j−i+x..j]}^iFk−1,x−1,j−i+x−1
Fk,i−1,j−1=Fk,i−2,j−1+∑i−1x=1,A[x..i−1]=B[j−i+x..j−1]Fk−1,x−1,j−i+x−1Fk,i-1,j-1 = Fk,i−2,j-1+\sum_{x=1,A[x..i-1]=B[j−i+x..j-1]}^{i-1}Fk−1,x−1,j−i+x−1
整理可得:
Fk,i,j=Fk,i−1,j,(A[i]!=B[j])Fk,i,j =Fk,i−1,j , (A[i] != B[j])
Fk,i,j=Fk,i−1,j+Fk,i−1,j−1−Fk,i−2,j−1+Fk−1,i−1,j−1,(A[i]=B[j])Fk,i,j= Fk,i−1,j + Fk,i−1,j−1 − Fk,i−2,j−1 + Fk−1,i−1,j−1 , (A[i] = B[j])
方法2
我们仍然设fk,i,j表示a串最后分开后的最后子串[l,r]<=i,b串匹配到j,且分了k段的方案数fk,i,j表示a串最后分开后的最后子串[l,r]<=i,b串匹配到j,且分了k段的方案数但是我们可以充分利用设来推方程
f[k,i,j]=∑j−1x=1f[k−1,i−1,x]+f[x,i−1,j−1]f[k,i,j]=\sum_{x=1}^{j-1}f[k-1,i-1,x]+f[x,i-1,j-1]
这样就可以得出一个简单易懂的方程,再加上前缀和优化便可以很好的解决问题了。
为什么这个方程是对的呢?
我们先想,若不分成一组,让ai,bjai,bj与前面合为一组,便是f[x,i−1,j−1]f[x,i-1,j-1]。
接着我们想前面的“最后分开后的最后子串[l,r]<=i”已经帮我们省去n的时间去匹配a串,故我们只需再枚举一次x(x<=j-1),便可以很好的解决问题了。
方法3:
(by cty考场70分方法)他和一开始我一样是设f[k,i,j]为a匹配到i,b匹配到j,分k组的方案数f[k,i,j]为a匹配到i,b匹配到j,分k组的方案数,但想到了前缀和优化,不过他还是枚举了x(x<=j-1)为a倒数x和b匹配即f[k−1,1..i−1,x]f[k-1,1..i-1,x]
若是他设得更好就可以100了。
总结
我一般都很少打noip原题的总结,但是这题却太过“优美”了。。其实我比赛时思维卡住的原因便是设的不够好,没有运用设去推!
从一开始方法0到方法3到方法2到方法1,这是思维的进步!
从开始枚举全部到枚举部分用前缀和再到用设去推再到观察妙用dp方程
这都是思维的突破,没有做不到只有想不到!
在以后做dp题时要多想状态之间的关系,和方程的优化,加油!
方法1:(by Philipsweng)
[code]#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 1005,MAXM = 205,Mo = int(1e9) + 7; char A[MAXN],B[MAXM]; int F[2][MAXN][MAXM],N,M,K; int main() { freopen("substring.in","r",stdin),freopen("substring.out","w",stdout); scanf("%d%d%d", &N, &M, &K); scanf("%s", A + 1); scanf("%s", B + 1); int cur = 0; for(int i = 0;i <= N;i ++) F[0][i][0] = 1; for(int k = 1;k <= K;k ++) { cur ^= 1; for(int i = 0;i <= N;i ++) F[cur][i][k - 1] = 0; for(int j = k;j <= M;j ++) for(int i = j;i <= N;i ++) { if (A[i] == B[j]) { F[cur][i][j] = (F[cur][i - 1][j - 1] + F[cur][i - 1][j]) % Mo; F[cur][i][j] = (F[cur][i][j] + F[cur ^ 1][i - 1][j - 1]) % Mo; if (i >= 2) F[cur][i][j] = (F[cur][i][j] - F[cur][i - 2][j - 1] + Mo) % Mo; } else F[cur][i][j] = F[cur][i - 1][j]; } } printf("%d\n", F[cur] [M]); return 0; }
方法2:
[code]const mo=1000000007; var n,m,r,i,j,k,l,t,x,y:longint; st,sr:ansistring; ans:longint; p:boolean; f,s:array[0..1,0..200,0..1000] of longint; function max(l,r:longint):longint; begin if l<r then exit(r);exit(l); end; function min(l,r:longint):longint; begin if l>r then exit(r);exit(l); end; begin assign(input,'substring.in');reset(input); assign(output,'substring.out');rewrite(output); readln(n,m,r); readln(st); readln(sr); f[0,0,0]:=1; for i:=0 to n do s[0,0,i]:=1; x:=1; for k:=1 to r do begin for i:=1 to m do for j:=1 to n do begin if sr[i]=st[j] then begin f[x,i,j]:=(s[1-x,i-1,j-1]+f[x,i-1,j-1]+f[x,i,j])mod mo; end; s[x,i,j]:=(f[x,i,j]+s[x,i,j-1])mod mo; end; x:=1-x; fillchar(f[x],sizeof(f[x]),0); fillchar(s[x],sizeof(s[x]),0); end; writeln(s[1-x,m,n]); close(input);close(output); end.
方法3:
[code]const mo=1000000007; var f,sum:array[0..1,0..200,0..1000] of int64; n,m,i,j,k,l,p,t,q:longint; ans:int64; s,st:ansistring; begin assign(input,'substring.in');reset(input); assign(output,'substring.out');rewrite(output); readln(n,m,k); readln(s); readln(st);f[0,0,0]:=1;q:=1; for i:=0 to n do sum[0,0,i]:=1; for t:=1 to k do begin for i:=1 to m do for j:=1 to n do begin if st[i]=s[j] then begin l:=j; while st[i-j+l]=s[l] do begin f[q,i,j]:=(f[q,i,j]+sum[1-q,i-j+l-1,l-1]) mod mo; dec(l);if (i-j+l=0) or (l=0) then break; end; end; sum[q,i,j]:=(sum[q,i,j-1]+f[q,i,j]) mod mo; end; q:=1-q;fillchar(f[q],sizeof(f[q]),0);fillchar(sum[q],sizeof(sum[q]),0); end; for i:=1 to n do ans:=(ans+f[1-q,m,i]) mod mo; writeln(ans); close(input);close(output); end.
相关文章推荐
- C#正则表达式通过HTML提取网页中的图片src
- 常用MIME类型
- Memcache分布式部署方案
- linux 下sort 命令排序
- 串行与并行、同步和异步串行通信
- 【C语言提高25】二级指针做输入的第二种内存模型:二维数组
- UVA1595-对称轴
- 1023. Have Fun with Numbers (20)
- 学习笔记(5)——文件压缩
- 1022. Digital Library (30)
- 如何评价rcnn、fast-rcnn和faster-rcnn这一系列方法?
- PAT乙级 数字分类 (20)
- Collatz问题
- matlab卷积相关
- 求最大公约数(辗转相除法)
- 初遇ACM之A+B问题
- Memcache服务器端参数说明
- LeetCode - Search in Rotated Sorted Array II
- Effective Objective-C 2.0 — 第五条用枚举表示状态、选项、状态码 (未看完)
- 最长01串