Codeforces 615C
传送门:http://codeforces.com/contest/615/problem/C
渣渣看题解写的,第一次学lcp,在这里再整理下。
题意:
给出两个字符串,每次可以从第一个字符串中截取一部分子串,要求用数目最少的子串拼接成第二个字符串,如果无法拼接,输出-1,否则输出子串的数目以及截取的区间。注意子串可以正序截取,也可以反序截取。
解题思路:
用二维数组 lcp[i][j] 表示 字符串 t 的由 i 到 lt (字符串 t 的长度)的部分与字符串 s 的由 j 到 ls 的部分的最长公共前缀长度。那么计算过程如下:
for(int i=lt;i>0;i--){ for(int j=ls;j>0;j--) if(t[i]==s[j]) lcp[i][j]=lcp[i+1][j+1]+1; }
因为题目中还可以反序截取,所以需要计算rlcp,同理:
for(int i=lt;i>0;i--){ for(int j=1;j<=ls;j++) if(t[i]==s[j]) rlcp[i][j]=rlcp[i+1][j-1]+1; }
为了偷懒,rlcp[i][j]的定义有了些许改动:为 t 的由 i 到 lt 部分与 s 的由 1 到 j 部分的最长公共前缀长度。
后面的只需要枚举 i ,选取最大值即可。详见代码。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int N=2110; char s[N],t[N]; int ls,lt,cnt,lcp[N][N],rlcp[N][N],L[N],R[N]; int main(){ //freopen("in.txt","r",stdin); while(~scanf("%s%s",s+1,t+1)){ ls=strlen(s+1),lt=strlen(t+1); memset(lcp,0,sizeof(lcp)); memset(rlcp,0,sizeof(rlcp)); for(int i=lt;i>0;i--){ for(int j=ls;j>0;j--) if(t[i]==s[j]) lcp[i][j]=lcp[i+1][j+1]+1; } for(int i=lt;i>0;i--){ for(int j=1;j<=ls;j++) if(t[i]==s[j]) rlcp[i][j]=rlcp[i+1][j-1]+1; } cnt=0; bool use_rev,flag=true; for(int i=1,mx=0,nxt;i<=lt;i+=mx){ mx=0,use_rev=false; for(int j=1;j<=ls;j++) if(lcp[i][j]>mx){ mx=lcp[i][j],nxt=j; } for(int j=1;j<=ls;j++) if(rlcp[i][j]>mx){ mx=rlcp[i][j],nxt=j,use_rev=true; } if(mx==0){ flag=false; break; } if(use_rev) R[cnt]=nxt-mx+1; else R[cnt]=nxt+mx-1; L[cnt++]=nxt; } if(flag){ printf("%d\n",cnt); for(int i=0;i<cnt;i++) printf("%d %d\n",L[i],R[i]); } else puts("-1"); } return 0; }