poj 1226 求多串最长公共子串 或 回文子串 KMP&&strstr&&后缀数组
2012-06-03 20:01
711 查看
题意:求给出的字符串中的最长公共子串 ,其中子串可以反转,比如rose 和 orchid , ro 和第一个匹配 , ro 的回文串or 和orchid 匹配 。最后输出最长子串的长度。
思路:找出长度最小的子串, 枚举其所有子串 ,然后将其反转再保存 ,最后用这两个串去匹配其它的字符串。
strstr 函数水的~ 注意strncpy枚举子串的方法
后缀数组的方法:和求多串公共子串差不多的方法,不过这题要连接的不只给出的字符串,也要把给出的子串的反转串连接起来,并且loc数组的状态要和正串是一样的。
最后二分枚举最短字符串的子串长度,判断即可。
思路:找出长度最小的子串, 枚举其所有子串 ,然后将其反转再保存 ,最后用这两个串去匹配其它的字符串。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; char str[52][505]; char ss[305] , ts1[305] , ts2[305]; int next[205]; void get_next(char *T , int len) { /*****得到next数组*****/ int j = 0, k = -1; next[0] = -1; while (j < len) { if(T[j] == T[k] || k == -1) { next[j+1] = k+1; j++;k++; } else k = next[k]; } } int kmp(char *T , int h , int len2 , int len1) { /*****KMP*****/ int i , j; i = j = 0; while(i <len1 && j <len2) { if(T[j]==str[h][i] || j == -1) { j++;i++; }else { j = next[j]; } } if(j == len2) return 1; return 0; } int main() { int T , i , j , k , len , n , g , temp; scanf("%d",&T); while(T--) { scanf("%d",&n); for(i = 0 ; i < n ; i ++) scanf("%s",str[i]); len = 120; for(i = 0 ; i < n ; i ++) { /*****找长度最小的字符串*****/ if(strlen(str[i]) <= len) { len = strlen(str[i]); temp = i; } } int tt , ans , cnt , l1 , l2 , lenn , f , flag , r , t; ans = 0; for(i = 1 ; i <= len ; i ++) { for(j = 0 ; j <= len - i; j ++) { for(g = 0 ,k = j ; k < j + i ; k ++ , g++) { /*****暴力枚举长度是i的最小字符串子串ts1*****/ ts1[g] = str[temp][k]; } ts1[g] = '\0'; for(tt = 0 ; tt < g ; tt ++) { /*****求这个子串的反转子串ts2*****/ ts2[tt] = ts1[g - tt - 1]; } ts2[tt] = '\0'; get_next(ts1 , i); /*****得到ts1的next数组*****/ cnt = 0; bool vis[100]; /*****标记数组*****/ memset(vis , false , sizeof(vis)); for(f = 0 ; f < n; f ++) { lenn = strlen(str[f]); if(kmp(ts1 , f , i , lenn) && !vis[f]) { vis[f] = 1; cnt++;} } get_next(ts2 , i); for(f = 0 ; f < n ; f ++) { lenn = strlen(str[f]); if(kmp(ts2 , f , i , lenn)&&!vis[f]) { vis[f] = 1; cnt++;} } /*****分别用ts1 、 ts1 去匹配其它串*****/ if(cnt == n) { //满足条件中取最长的子串长度 if(i > ans) ans = i; } } } printf("%d\n",ans); } }
strstr 函数水的~ 注意strncpy枚举子串的方法
#include<iostream> #include<cstdio> #include<cstring> using namespace std; char str[120][120]; char s1[120] , s2[120]; char zz1[120] , zz2[120]; int main() { int n , T , i , j , k , t , temp; scanf("%d",&T); while(T--) { scanf("%d",&n); int len = 120; for(i = 0 ; i < n ; i ++) { scanf("%s",str[i]); int ll = strlen(str[i]); if(ll < len) { len = ll; temp = i; } } strcpy(s1 , str[temp]); int ans = 0; for(i = 1 ; i <= len ; i ++) { //枚举子串长度 for(j = 0 ; j <= len - i ; j ++) { strncpy(zz1, s1+j , i); zz1[i] = '\0'; //子串 //printf("%s\n",zz1); for(k = 0 ; k < i ; k ++) zz2[k] = zz1[i - k - 1]; zz2[k] = '\0'; //反转子串 //printf("%s\n",zz2); for(t = 0 ; t < n ; t ++) { if(strstr(str[t] , zz1)||strstr(str[t] , zz2)) continue; else break; } if(t == n) ans = i>ans?i:ans; } } printf("%d\n",ans); } }
后缀数组的方法:和求多串公共子串差不多的方法,不过这题要连接的不只给出的字符串,也要把给出的子串的反转串连接起来,并且loc数组的状态要和正串是一样的。
最后二分枚举最短字符串的子串长度,判断即可。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn = 21000; char str[maxn]; int num[maxn] , loc[maxn]; int sa[maxn] , rank[maxn] , height[maxn]; int wa[maxn] , wb[maxn] , wv[maxn] , wd[maxn]; int n; int cmp(int *r , int a , int b , int l) { return r[a] == r[b] && r[a+l] == r[b+l]; } void da(int *r , int n , int m) { int i , j , p; int *x = wa , *y = wb , *t; for(i = 0 ; i < m ; i ++) wd[i] = 0; for(i = 0 ; i < n ; i ++) wd[x[i]=r[i]]++; for(i = 1 ; i < m ; i ++) wd[i] += wd[i-1]; for(i = n-1 ; i >= 0 ; i --) sa[--wd[x[i]]] = i; for(j = 1 , p = 1 ; p < n ; j *= 2 , m = p) { for(p = 0 , i = n - j ; i < n ; i ++) y[p++] = i; for(i = 0 ; i < n ; i ++) if(sa[i] >= j) y[p++] = sa[i] - j; for(i = 0 ; i < n ; i ++) wv[i] = x[y[i]]; for(i = 0 ; i < m ; i ++) wd[i] = 0; for(i = 0 ; i < n ; i ++) wd[wv[i]] ++; for(i = 1 ; i < m ; i ++) wd[i] += wd[i-1]; for(i = n - 1 ; i >= 0 ; i --) sa[--wd[wv[i]]] = y[i]; for(t = x , x = y , y = t , p = 1 , x[sa[0]] = 0 , i = 1 ; i < n ; i ++) { x[sa[i]] = cmp(y , sa[i-1] , sa[i] , j) ? p - 1: p ++; } } } void calheight(int *r , int n) { int i , j , k = 0; for(i = 1 ; i <= n ; i ++) rank[sa[i]] = i; for(i = 0 ; i < n ; height[rank[i++]] = k) { for(k ? k --: 0 , j = sa[rank[i]-1] ; r[i+k]==r[j+k] ; k ++); } } int vis[1300]; bool check(int mid , int len) { int i , j , cnt; cnt = 0; memset(vis , 0 , sizeof(vis)); for(i = 2 ; i <= len ; i ++) { //printf("%d\n",height[i]); if(height[i] < mid) { memset(vis,0,sizeof(vis)); cnt = 0; continue; } if(!vis[loc[sa[i-1]]]) { vis[loc[sa[i-1]]] = 1; cnt ++; } if(!vis[loc[sa[i]]]) { vis[loc[sa[i]]] = 1; cnt ++; } if(cnt == n) return 1; } return 0; } int main() { int T , i , j , g , k , temp; scanf("%d",&T); while(T--) { scanf("%d",&n); g = 0; temp = 130; int ll = 120; for(i = 0 ; i < n ; i ++) { scanf("%s",str); int len = strlen(str); if(len < ll) ll = len; for(j = 0 ; j < len ; j ++) { loc[g] = i; num[g++] = str[j]; } loc[g] = temp; num[g++] = temp++; for(j = 0 ; j < len ; j ++) { loc[g] = i; num[g++] = str[len - j - 1]; } loc[g] = temp; num[g++] = temp ++; } num[g] = 0; da(num , g+1 , temp); calheight(num , g); int left , right , mid , ans; left = 0;right = ll; while(left <= right) { mid = (left+right)/2; if(check(mid , g)) { left = mid + 1; ans = mid; } else { right = mid - 1; //ans = mid; } } printf("%d\n",ans); } }
相关文章推荐
- poj 1226 出现或反转后出现在每个字符串中的最长子串(后缀数组/kmp)
- hdu1403 && poj 2774 最长公共子串 后缀数组
- POJ 1226 Substrings (后缀数组 n个串的最长公共子串)
- POJ 2774 后缀数组:求最长公共子串
- poj 2774 后缀数组 最长公共子串
- poj 2774 最长公共子串 后缀数组
- POJ 2774 Long Long Message 求两个串最长公共子串(后缀数组)
- poj 2774 最长公共子串 后缀数组
- POJ 2774 Long Long Message (后缀数组求最长公共子串)
- POJ 2774 Long Long Message(后缀数组[最长公共子串])
- poj 2774 后缀数组 两个字符串的最长公共子串
- 【后缀数组】 POJ 2774 Long Long Message 两个字符串的最长公共子串长度
- 后缀数组之最长公共子串 poj 2774
- Poj 3294 Life Forms (后缀数组 在n个串中出现k次的最长公共子串并输出)
- POJ - 2774 Long Long Message (最长公共子串 后缀数组)
- POJ-2774-Long Long Message(后缀数组-最长公共子串)
- POJ 题目3294Life Forms(后缀数组求超过k个的串的最长公共子串)
- POJ 2774 Long Long Message(后缀数组求最长公共子串,4级)
- Long Long Message+POJ+后缀数组之求最长公共子串
- poj 2774 最长公共子串 后缀数组