poj 1226 Substrings // poj 3450 Corporate Identidy KMP + 二分枚举
poj 1226 Substrings
http://poj.org/problem?id=1226
/* 题目: 查找最长子串在所有给出的字符串或者它的反转的串中出现 分析: 二分枚举所有可能的长度,然后用KMP算法判断该枚举的子串是否在所有给出的字符串 或者它的翻转串中出现。 */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int X = 205; char in[X][X],p[X]; int fail[X],len[X],n,lens; int mid; //mid 表示枚举的长度 int ma,pos; //ma表示所有串中长度最小的,pos表示该最短串的位置 void shift() //模式串先与自己进行模式匹配,算出fail指针 { fail[0] = -1; int j = -1; for(int i=1;i<mid;i++) { while(j!=-1&&p[i]!=p[j+1]) j = fail[j]; if(p[i]==p[j+1]) j++; fail[i] = j; } } bool kmp(char *s) //模式串与文本串进行模式匹配,判断是否在文本串中出现模式串 { int j = -1; for(int i=0;i<lens;i++) { while(j!=-1&&s[i]!=p[j+1]) j = fail[j]; if(s[i]==p[j+1]) j++; if(j+1==mid) return true; } return false; } void solve() //二分枚举函数 { int right = ma,left = 0; int temp; while(left<=right) { bool ok = true; mid = (left+right+1)>>1; temp = len[pos]-mid; for(int i=0;i<=temp;i++) { int cnt = 0; for(int j=i;j<mid+i;j++) //从该位开始枚举所有该长度的子串 p[cnt++] = in[pos][j]; p[mid] = '\0'; //结束 shift(); //计算fail指针 ok = true; for(int j=0;j<n;j+=2) { if(j==pos) continue; lens = len[j]; if(kmp(in[j])||kmp(in[j+1]))//如果当前的串或反串中出现了枚举的串 continue; ok = false; break; } if(ok) //如果所有都符合可直接跳出 break; } if(ok) left = mid+1; else right = mid-1; } cout<<right<<endl; } int main() { freopen("sum.in","r",stdin); freopen("sum.out","w",stdout); int ncase,cnt; cin>>ncase; while(ncase--) { ma = 1000; scanf("%d ",&n); n = n<<1; for(int i=0;i<n;i+=2) { cnt = 0; gets(in[i]); len[i] = len[i+1] = strlen(in[i]); if(len[i]<ma) { ma = len[i]; pos = i; } for(int j=len[i]-1;j>=0;j--) //求反串 in[i+1][cnt++] = in[i][j]; in[i+1][cnt] = '\0'; } solve(); } return 0; }
相同一种类型的题 poj 3450 Corporate Identity
http://poj.org/problem?id=3450
/* 题目: 给出n串字符串,求他们的最长公共子串,若有相同长度的话,输出字典序最小的 分析: 二分+KMP,先找出所有字符串中最小长度的串,二分枚举该串所有长度的子串,然后用KMP 判断是否所有的字符串均含有该子串,有的话,更新答案 */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int NUM = 4005; const int LEN = 205; char in[NUM][LEN],ans[LEN],p[LEN]; int fail[LEN],len[NUM],n,lens,mid,ma,pos; void shift() { fail[0] = -1; int j = -1; for(int i=1;i<mid;i++) { while(j!=-1&&p[i]!=p[j+1]) j = fail[j]; if(p[i]==p[j+1]) j++; fail[i] = j; } } bool kmp(char *s) { int j = -1; for(int i=0;i<lens;i++) { while(j!=-1&&s[i]!=p[j+1]) j = fail[j]; if(s[i]==p[j+1]) j++; if(j+1==mid) return true; } return false; } void solve() //二分枚举 { int left = 0,right = ma; int temp,cnt; bool ans_flag = false; bool ok; while(left<=right) { mid = (left+right+1)>>1; temp = len[pos]-mid; ok = true; bool flag = false; for(int i=0;i<=temp;i++) { cnt = 0; for(int j=i;j<mid+i;j++) p[cnt++] = in[pos][j]; p[mid] = '\0'; shift(); ok = true; for(int j=0;j<n;j++) { if(j==pos) continue; lens = len[j]; if(kmp(in[j])) continue; ok = false; break; } if(ok) //所有的串都拥有该子串,更新答案 { if(ans_flag) { if(flag&&strcmp(ans,p)>0) strcpy(ans,p); else if(!flag) { flag = true; strcpy(ans,p); } } else { flag = true; ans_flag = true; strcpy(ans,p); } } } if(flag) left = mid+1; else right = mid-1; } if(right) printf("%s\n",ans); else printf("IDENTITY LOST\n"); } int main() { freopen("sum.in","r",stdin); freopen("sum.out","w",stdout); while(scanf("%d ",&n),n) { ma = 10000; for(int i=0;i<n;i++) { gets(in[i]); len[i] = strlen(in[i]); if(ma>len[i]) { ma = len[i]; pos = i; } } solve(); } return 0; }