题意:就是求多串的最长公共子串
思路:子串可以看作是字符串中后缀的前缀。把这些字符串连接起来,中间用不能出现的状态隔开,然后求其height数组,二分枚举一个字符串的子串长度,看是否存在连续n个最长公共前缀的长度都大于mid,并且这些公共长度分别属于n个字符串。
poj 3080
保证最长的前提下,保证字典序,字典序是用sa数组来保证的,最先找到的公共前缀一定是字典序较小的,然后这些公共前缀的开头都是一样的,所以输出的公共子串的开头也是以sa[i]为头的。。。。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 800100;
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 T;
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[4010];
char ss[maxn];
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 %d\n",height[i],sa[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==T) {
for(j = 0 ; j < mid ; j ++)
ss[j] = num[sa[i]+j] + 'A' - 2;
return 1;
} ss[mid] = '\0';
}
return 0;
}
int main() {
int i , len , g , j , N;
scanf("%d",&N);
while(N--)
{
scanf("%d",&T);
//if(T==0) break;
g = 0;
int temp = 30;
for(i = 0 ; i < T ; i ++) {
scanf("%s",str);
len = strlen(str);
for(j = 0 ; j < len ; j ++) {
loc[g] = i;//标记字符串块儿
num[g++] = str[j] - 'A' + 2;
}
loc[g] = temp;
num[g++] = temp++; //一些不会出现的状态
}
//loc[g]=-1;
num[g] = 0;
da(num , g+1 , temp);
calheight(num , g);
int left = 0 , right = strlen(str) , mid;
int ans = 0;
while(left <= right) {
mid = (left+right)/2;
if(check(mid , g)) {
left = mid + 1;
} else {
right = mid - 1;
}
}
if(strlen(ss)>=3)
printf("%s\n",ss);
else printf("no significant commonalities\n");
}
}
poj 3450 基本是一个题
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 800100;
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 T;
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[4010];
char ss[maxn];
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 %d\n",height[i],sa[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==T) {
for(j = 0 ; j < mid ; j ++)
ss[j] = num[sa[i]+j] + 'a' - 2;
return 1;
} ss[mid] = '\0';
}
return 0;
}
int main() {
int i , len , g , j;
while(scanf("%d",&T)!=EOF) {
if(T==0) break;
g = 0;
int temp = 30;
for(i = 0 ; i < T ; i ++) {
scanf("%s",str);
len = strlen(str);
for(j = 0 ; j < len ; j ++) {
loc[g] = i;//标记字符串块儿
num[g++] = str[j] - 'a' + 2;
}
loc[g] = temp;
num[g++] = temp++; //一些不会出现的状态
}
//loc[g]=-1;
num[g] = 0;
da(num , g+1 , temp);
calheight(num , g);
int left = 0 , right = strlen(str) , mid;
int ans = 0;
while(left <= right) {
mid = (left+right)/2;
if(check(mid , g)) {
left = mid + 1;
ans = mid;
} else {
right = mid - 1;
}
}
if(ans!=0)
printf("%s\n",ss);
else printf("IDENTITY LOST\n");
}
}