求N个字符串的最大公共子串

LeonTown 2009-08-26 08:13:04
如题,
两个字符串,可以用DP的方法求最大公共子串。
但对N个字符串,需要比较(N-1)!次吗?
还是有更高效的方法?
...全文
1065 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
linren 2009-08-28
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 bjtumach 的回复:]
linren

linren2
是一个人不??
[/Quote]
呵呵
是同一个人……
linren2 2009-08-28
  • 打赏
  • 举报
回复
20楼的程序的一个修改版:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**只用修改这里**************************/
char a[]="aaacbb";
char b[]="aaabbc";
char c[]="bbaccc";
char d[]="bbbbbb";
/**************************只用修改这里**/
struct node{
char *s;
int len;
};

struct save{
struct node *a;
int cnt;
int size;
};

void init_save(struct save *sv,int size){
sv->size=size;
sv->cnt=0;
sv->a=(struct node*)malloc(sizeof(struct node)*size);
}
void del_save(struct save *sv){
free(sv->a);
}

void add_save(struct save *sv,char *s){
if(sv->cnt==sv->size){
printf("error: space is full\n");return;
}
sv->a[sv->cnt].len=strlen(s);
sv->a[sv->cnt].s=s;
sv->cnt++;
}

int compar(const void *a,const void *b){
char **aa,**bb;
int cmp=0;
aa=(char**)a;
bb=(char**)b;
cmp=strcmp(*aa,*bb);
if(cmp>0) return 1;
if(cmp==0) return 0;
return -1;
}

void fun(struct save *sv){
int i,j,n;
int e,d;
char **p;
char *t,*tt,*tt2;
n=0;
for(i=0;i<sv->cnt;i++) n+=sv->a[i].len;
n+=(sv->cnt-1);

t=(char*)malloc(sizeof(char)*(n+1));
tt=(char*)malloc(sizeof(char)*(sv->a[0].len+1));
tt2=(char*)malloc(sizeof(char)*(sv->a[0].len+1));
p=(char**)malloc(sizeof(char*)*(n+1));

for(i=0,j=0;i<(sv->cnt-1);i++){
strcpy(t+j,sv->a[i].s);
j+=sv->a[i].len;
t[j]=' ';
j++;
}
strcpy(t+j,sv->a[i].s);
memset(tt,0,sizeof(char)*(sv->a[0].len+1));
memset(tt2,0,sizeof(char)*(sv->a[0].len+1));
memset(p,0,sizeof(char*)*n);

for(i=0;i<n;i++) p[i]=&t[i];
qsort(p,n,sizeof(char*),compar);

e=-1;
for(i=0;i<n-1;i++){
d=0;
memset(tt,0,sizeof(char)*(sv->a[0].len+1));
for(j=0;*(*(p+i)+j)==*(*(p+i+1)+j);j++){
tt[j]=*(*(p+i)+j);d++;
}
if(d==0||d<=e) continue;
for(j=0;j<sv->cnt;j++){
if(strstr(sv->a[j].s,tt)==0) break;
}
if(j!=sv->cnt) continue;
e=d;
strcpy(tt2,tt);
}

printf("fun( ");
for(i=0;i<sv->cnt-1;i++) printf("%s, ",sv->a[i].s);
printf("%s ) = %s\n",sv->a[i].s,tt2);
free(t);free(p);free(tt);free(tt2);
}

int main(){
struct save sv;
init_save(&sv,10);
add_save(&sv,a);
add_save(&sv,b);
add_save(&sv,c);
add_save(&sv,d);

fun(&sv);

del_save(&sv);return 0;
}

修正了一些BUG……
并且优化了一下算法……
BJTUMach 2009-08-28
  • 打赏
  • 举报
回复
linren

linren2
是一个人不??
linren 2009-08-28
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 leontown 的回复:]
linren太牛了。。。
呵呵
[/Quote]
是NaWait和BJTUMach提供了方法^_^
我想到的方法都很笨的……

【程序】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**只用修改这里**************************/
char a[]="aaaaa";
char b[]="aaaaa";
char c[]="aaaaa";
char d[]="aaaaa";
/**************************只用修改这里**/
struct node{
char *s;
int len;
};

struct save{
struct node *a;
int cnt;
int size;
};

void init_save(struct save *sv,int size){
sv->size=size;
sv->cnt=0;
sv->a=(struct node*)malloc(sizeof(struct node)*size);
}
void del_save(struct save *sv){
free(sv->a);
}

void add_save(struct save *sv,char *s){
if(sv->cnt==sv->size){
printf("error: space is full\n");return;
}
sv->a[sv->cnt].len=strlen(s);
sv->a[sv->cnt].s=s;
sv->cnt++;
}

int compar(const void *a,const void *b){
char **aa,**bb;
int cmp=0;
aa=(char**)a;
bb=(char**)b;
cmp=strcmp(*aa,*bb);
if(cmp>0) return 1;
if(cmp==0) return 0;
return -1;
}

void fun(struct save *sv){
int i,j,n;
int e,d;
char **p;
char *t,*tt,*tt2;
n=0;
for(i=0;i<sv->cnt;i++) n+=sv->a[i].len;
n+=(sv->cnt-1);

t=(char*)malloc(sizeof(char)*(n+1));
tt=(char*)malloc(sizeof(char)*(sv->a[0].len+1));
tt2=(char*)malloc(sizeof(char)*(sv->a[0].len+1));
p=(char**)malloc(sizeof(char*)*(n+1));

for(i=0,j=0;i<(sv->cnt-1);i++){
strcpy(t+j,sv->a[i].s);
j+=sv->a[i].len;
t[j]=' ';
j++;
}
strcpy(t+j,sv->a[i].s);
memset(tt,0,sizeof(char)*(sv->a[0].len+1));
memset(tt2,0,sizeof(char)*(sv->a[0].len+1));
memset(p,0,sizeof(char*)*n);

for(i=0;i<n;i++) p[i]=&t[i];
qsort(p,n,sizeof(char*),compar);

e=-1;
for(i=0;i<n-1;i++){
d=0;
memset(tt,0,sizeof(char)*(sv->a[0].len+1));
for(j=0;*(*(p+i)+j)==*(*(p+i+1)+j)&&*(*(p+i)+j)!=' ';j++){
tt[j]=*(*(p+i)+j);d++;
}
if(d==0||d<=e) continue;
for(j=0;j<sv->cnt;j++){
if(strstr(sv->a[j].s,tt)==0) break;
}
if(j!=sv->cnt) continue;
e=d;
strcpy(tt2,tt);
}

printf("fun( ");
for(i=0;i<sv->cnt-1;i++) printf("%s, ",sv->a[i].s);
printf("%s ) = %s\n",sv->a[i].s,tt2);
free(t);free(p);free(tt);free(tt2);
}

int main(){
struct save sv;
init_save(&sv,10);
add_save(&sv,a);
add_save(&sv,b);
add_save(&sv,c);
add_save(&sv,d);

fun(&sv);

del_save(&sv);return 0;
}

【说明】
22楼的程序有一个缓冲区溢出的BUG……
这个是修正后的版本……
LeonTown 2009-08-28
  • 打赏
  • 举报
回复
linren太牛了。。。
呵呵
showjim 2009-08-28
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 litaoye 的回复:]
用trie(字母树)应该可以快速求解,LZ看看相关资料吧,没有时间仔细想。
感觉用后缀树有些大材小用了。
[/Quote]
算法也有阶段之分?合用就好吧
绿色夹克衫 2009-08-28
  • 打赏
  • 举报
回复
用trie(字母树)应该可以快速求解,LZ看看相关资料吧,没有时间仔细想。
感觉用后缀树有些大材小用了。
linren 2009-08-27
  • 打赏
  • 举报
回复
(上接20楼)
【说明】
假如n个字符串的长度分别为l1,l2,...,ln……

建立后缀数组需要的时间为:
对长度为(l1+l2+...+ln)+(n-1)的数组进行排序的时间(20楼使用的是qsort快速排序)……

然后是遍历后缀数组……
判断相邻的两个后缀的相同前缀是否在1~n个字符串内都存在……

如果都存在则筛选最优结果……
这样在遍历完后得到的即是最优结果……
LeonTown 2009-08-27
  • 打赏
  • 举报
回复
我想的就是,
N个串,先横向的两两之间用DP的方法比较,可能需要保留若干候选解;
然后,再纵向从所有两两的候选解中,可能还要通过DP得到最终解。
LeonTown 2009-08-27
  • 打赏
  • 举报
回复
谢谢楼上的回复。

我这里是希望得到longest common substring,
就是这个子串是连续的,N个串中都包含。
linren 2009-08-27
  • 打赏
  • 举报
回复
【程序】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**只用修改这里**************************/
char a[]="aaacbb";
char b[]="aaabbc";
char c[]="bbaccc";
char d[]="bbbbbb";
/**************************只用修改这里**/
struct node{
char *s;
int len;
};

struct save{
struct node *a;
int cnt;
int size;
};

void init_save(struct save *sv,int size){
sv->size=size;
sv->cnt=0;
sv->a=(struct node*)malloc(sizeof(struct node)*size);
}
void del_save(struct save *sv){
free(sv->a);
}

void add_save(struct save *sv,char *s){
if(sv->cnt==sv->size){
printf("error: space is full\n");return;
}
sv->a[sv->cnt].len=strlen(s);
sv->a[sv->cnt].s=s;
sv->cnt++;
}

int compar(const void *a,const void *b){
char **aa,**bb;
int cmp=0;
aa=(char**)a;
bb=(char**)b;
cmp=strcmp(*aa,*bb);
if(cmp>0) return 1;
if(cmp==0) return 0;
return -1;
}

void fun(struct save *sv){
int i,j,na,nb,nc,n;
int e,d;
char **p;
char *t,*tt,*tt2;
n=0;
for(i=0;i<sv->cnt;i++) n+=sv->a[i].len;
na=strlen(a);nb=strlen(b);nc=strlen(c);
n+=(sv->cnt-1);

t=(char*)malloc(sizeof(char)*(n+1));
tt=(char*)malloc(sizeof(char)*(sv->a[0].len+1));
tt2=(char*)malloc(sizeof(char)*(sv->a[0].len+1));
p=(char**)malloc(sizeof(char*)*(n+1));

for(i=0,j=0;i<(sv->cnt-1);i++){
strcpy(t+j,sv->a[i].s);
j+=sv->a[i].len;
t[j]=' ';
j++;
}
strcpy(t+j,sv->a[i].s);
memset(tt,0,sizeof(char)*(sv->a[0].len+1));
memset(tt2,0,sizeof(char)*(sv->a[0].len+1));
memset(p,0,sizeof(char*)*n);

for(i=0;i<n;i++) p[i]=&t[i];
qsort(p,n,sizeof(char*),compar);

e=-1;
for(i=0;i<n-1;i++){
d=0;
memset(tt,0,sizeof(char)*(sv->a[0].len+1));
for(j=0;*(*(p+i)+j)==*(*(p+i+1)+j);j++){
tt[j]=*(*(p+i)+j);d++;
}
if(d==0) continue;
for(j=0;j<sv->cnt;j++){
if(strstr(sv->a[j].s,tt)==0) break;
}
if(j!=sv->cnt) continue;
if(e==-1||d>e){
e=d;
strcpy(tt2,tt);
}
}

printf("fun( ");
for(i=0;i<sv->cnt-1;i++) printf("%s, ",sv->a[i].s);
printf("%s ) = %s\n",sv->a[i].s,tt2);
free(t);free(p);free(tt);free(tt2);
}

int main(){
struct save sv;
init_save(&sv,10);
add_save(&sv,a);
add_save(&sv,b);
add_save(&sv,c);
add_save(&sv,d);

fun(&sv);

del_save(&sv);return 0;
}

【运行结果】
fun( aaacbb, aaabbc, bbaccc, bbbbbb ) = bb
Press any key to continue
NaWait 2009-08-27
  • 打赏
  • 举报
回复
如果是說 longest common subsequence 那我說上面方法得應該可行

如果是說 longest common substring 那可能要用 Suffix tree 吧
showjim 2009-08-27
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 nawait 的回复:]
引用 5 楼 tanwan 的回复:
如果a1和a2找到最大串并不是和a3也是最大串吧?


大大我不太懂你的意思耶  可以舉個例子嗎?
[/Quote]
错误:
aaacbb&aaabbc=aaa
aaa&bbaccc=a
正确
aaacbb&aaabbc&bbaccc=bb
NaWait 2009-08-27
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 tanwan 的回复:]
如果a1和a2找到最大串并不是和a3也是最大串吧?
[/Quote]

大大我不太懂你的意思耶 可以舉個例子嗎?
showjim 2009-08-27
  • 打赏
  • 举报
回复
当字符串的个数大于最小字符串长度的2次方时,可以将最小字符串拆成N*(N+1)/2个子串做hash统计.
在这个基础上当字符串的个数大于128的时候,可以针对最小字符串做字符映射位图,算法的本质类似于脏词过滤程序.
linren 2009-08-27
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 bjtumach 的回复:]
引用 17 楼 linren 的回复:
引用 16 楼 bjtumach 的回复:
如果是求N个串的最长连续子串的话用后缀数组
O(nlog(n))

后缀数组可以解决求2个字符串的最大公共子串……

但是主要问题在于如何把2字符串个推广到多个字符串……
10楼提供的例子说明归并的方法是不可行的……

http://acm.pku.edu.cn/JudgeOnline/problem?id=3080
看这道题,它就可以用后缀数组完成,采用倍增构造后缀树,可以实现
当然简单点sort也可以O(N^2)
[/Quote]

的确是可以的……
真的是个不错的方法……

【程序】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**只用修改这里**************************/
char a[]="aaacbb";
char b[]="aaabbc";
char c[]="bbaccc";
/**************************只用修改这里**/

int compar(const void *a,const void *b){
char **aa,**bb;
int cmp=0;
aa=(char**)a;
bb=(char**)b;
cmp=strcmp(*aa,*bb);
if(cmp>0) return 1;
if(cmp==0) return 0;
return -1;
}

void fun2(char *a,char *b){
int i,j,na,nb,n;
int c,d;
char **p;
char *t,*tt,*tt2;

na=strlen(a);nb=strlen(b);
n=na+nb+1;

t=(char*)malloc(sizeof(char)*(n+1));
tt=(char*)malloc(sizeof(char)*(na+1));
tt2=(char*)malloc(sizeof(char)*(na+1));
p=(char**)malloc(sizeof(char*)*(n+1));

strcpy(t,a);t[na]=' ';strcpy(t+na+1,b);
memset(tt,0,sizeof(char)*(na+1));
memset(tt2,0,sizeof(char)*(na+1));
memset(p,0,sizeof(char*)*n);

for(i=0;i<n;i++) p[i]=&t[i];
qsort(p,n,sizeof(char*),compar);

c=-1;
for(i=0;i<n-1;i++){
d=0;
memset(tt,0,sizeof(char)*(na+1));
for(j=0;*(*(p+i)+j)==*(*(p+i+1)+j);j++){
tt[j]=*(*(p+i)+j);d++;
}
if(d==0) continue;
if(strstr(a,tt)==0) continue;
if(strstr(b,tt)==0) continue;
if(c==-1||d>c){
c=d;
strcpy(tt2,tt);
}
}

printf("fun2( %s, %s ) = %s\n",a,b,tt2);
free(t);free(p);free(tt);free(tt2);
}


void fun3(char *a,char *b,char *c){
int i,j,na,nb,nc,n;
int e,d;
char **p;
char *t,*tt,*tt2;

na=strlen(a);nb=strlen(b);nc=strlen(c);
n=na+nb+nc+2;

t=(char*)malloc(sizeof(char)*(n+1));
tt=(char*)malloc(sizeof(char)*(na+1));
tt2=(char*)malloc(sizeof(char)*(na+1));
p=(char**)malloc(sizeof(char*)*(n+1));

strcpy(t,a);t[na]=' ';
strcpy(t+na+1,b);t[na+nb+1]=' ';
strcpy(t+na+nb+2,c);
memset(tt,0,sizeof(char)*(na+1));
memset(tt2,0,sizeof(char)*(na+1));
memset(p,0,sizeof(char*)*n);

for(i=0;i<n;i++) p[i]=&t[i];
qsort(p,n,sizeof(char*),compar);

e=-1;
for(i=0;i<n-1;i++){
d=0;
memset(tt,0,sizeof(char)*(na+1));
for(j=0;*(*(p+i)+j)==*(*(p+i+1)+j);j++){
tt[j]=*(*(p+i)+j);d++;
}
if(d==0) continue;
if(strstr(a,tt)==0) continue;
if(strstr(b,tt)==0) continue;
if(strstr(c,tt)==0) continue;
if(e==-1||d>e){
e=d;
strcpy(tt2,tt);
}
}

printf("fun3( %s, %s, %s ) = %s\n",a,b,c,tt2);
free(t);free(p);free(tt);free(tt2);
}

int main(){
fun2(a,b);
fun3(a,b,c);
return 0;
}

【运行结果】
fun2( aaacbb, aaabbc ) = aaa
fun3( aaacbb, aaabbc, bbaccc ) = bb
Press any key to continue
绿色夹克衫 2009-08-27
  • 打赏
  • 举报
回复
这个公共字串的概念是什么?是N个字符串都有的串?还是两两之间公共字串中最大的那个?
LeonTown 2009-08-27
  • 打赏
  • 举报
回复
有可能N个串中没有公共子串。

但,我们可以先简单考虑,
假设N个串中有公共子串。

那需要比较多少次呢?
第一个跟后面N-1个分别比较;
第二个跟后面N-2个分别比较;
那岂不是(N-1)!次。

另外,我还没有去查算法导论上的相关章节。
不是不接受楼上大侠的意见。
接下来,我会查一下。
也请大家多给意见。
BJTUMach 2009-08-27
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 linren 的回复:]
引用 16 楼 bjtumach 的回复:
如果是求N个串的最长连续子串的话用后缀数组
O(nlog(n))

后缀数组可以解决求2个字符串的最大公共子串……

但是主要问题在于如何把2字符串个推广到多个字符串……
10楼提供的例子说明归并的方法是不可行的……
[/Quote]
http://acm.pku.edu.cn/JudgeOnline/problem?id=3080
看这道题,它就可以用后缀数组完成,采用倍增构造后缀树,可以实现
当然简单点sort也可以O(N^2)
linren 2009-08-27
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 bjtumach 的回复:]
如果是求N个串的最长连续子串的话用后缀数组
O(nlog(n))
[/Quote]
后缀数组可以解决求2个字符串的最大公共子串……

但是主要问题在于如何把2字符串个推广到多个字符串……
10楼提供的例子说明归并的方法是不可行的……
加载更多回复(8)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧