POJ1226 - Substrings(KMP+二分)
题目大意
给定n个字符串,字符串可逆序可顺序,求它们的最长公共子串
题解
在输入的过程中记录一下最短的那个字符串,然后枚举起点,然后进行二分求出子串末位置,然后再验证是否是公共子串,记录最长的公共子串就是最终答案~~~~时间复杂度为O(N^3*logn)
代码:
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; #define MAXN 105 #define INF 0x3f3f3f3f char T[MAXN][MAXN]; int f[MAXN]; void getfail(char *p,int len) { int j; f[0]=j=-1; for(int i=1;i<len;i++) { while(j>=0&&p[j+1]!=p[i]) j=f[j]; if(p[j+1]==p[i])j++; f[i]=j; } } bool find(char *s,int len,int n) { getfail(s,len); for(int i=0;i<n;i++) { int j=-1,lt=strlen(T[i]); bool flag1=false,flag2=false; for(int t=0;t<lt;t++) { while(j>=0&&s[j+1]!=T[i][t]) j=f[j]; if(s[j+1]==T[i][t]) j++; if(j+1==len) { flag1=true; break; } } char ss[MAXN]; strcpy(ss,T[i]); reverse(ss,ss+lt); j=-1; for(int t=0;t<lt;t++) { while(j>=0&&s[j+1]!=ss[t]) j=f[j]; if(s[j+1]==ss[t]) j++; if(j+1==len) { flag2=true; break; } } if(!flag1&&!flag2) return false; } return true; } int main() { int cases; scanf("%d",&cases); while(cases--) { int n,maxlen=-INF,len=INF; char s[MAXN]; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%s",T[i]); if(len>strlen(T[i])) { len=strlen(T[i]); strcpy(s,T[i]); } } for(int i=0;i<len;i++) { int l=i,r=len-1,lens=-INF; while(l<=r) { int mid=(l+r)>>1; bool flag=find(s+i,mid-i+1,n); if(flag) { l=mid+1; lens=mid-i+1; } else r=mid-1; if(lens!=-INF&&lens>maxlen) maxlen=lens; } } if(maxlen!=-INF) printf("%d\n",maxlen); else { printf("0\n"); } } return 0; }