找出字符串中最长的重复子串(三种解法)

本文介绍了三种找出字符串中最长重复子串的方法:1)通过遍历比较字符串;2)利用后缀数组和快速排序;3)使用两层循环结合substring()方法。详细阐述了每种方法的实现思路和代码示例。

******************************************************************************
第一种解法:
******************************************************************************
比如 输入qweabcuwabcfw,输出结果为3 第二次出现abc的 a的地址。//不用查找函数

int maxsubstr(char* src,char** p)
{
	char *sztmp = src;
	char *tmp = NULL;  //临时字符串,如ca,ab,等
	int i, j, k;   //用于循环
	int max_len = 1, count = 0, ipos = 0;
	//统计字符串长度,统计重复次数,统计位置
	int len;  //字符串长度
	if (sztmp == NULL||(len = strlen(sztmp))==0){//判断源字符串是否合格
		return -1;
	}
	tmp = (char*)malloc(sizeof(char)*len+1);
	memset(tmp,0,len+1);


	for (i=1;i<len;i++){//获取字符长度
		for (j=0;j<len-i-1;j++){//截取源字符串中i长度的字符串
			strncpy(tmp,sztmp+j,i+1);
			for (k=0,count=0;k<len-i;k++){
			//循环比较字符串,如有重复字符串,并且最长的记录下搜索来
				if (strncmp((sztmp+k),tmp,i+1)==0){
					count++;
					if (count != 1){
						if (i >= max_len){
							max_len = i+1;
							ipos = k;
						}
					}
				}
			}
		}
	}
	free(tmp);
	*p = sztmp + ipos;
	return max_len;
}


int main(){
	char* p = NULL;
	int n = maxsubstr("qwiohiuweowohifpw",&p);
	printf("%d\n %p\n", n, &p);
}



******************************************************************************
第二种解法:
******************************************************************************
思路:使用后缀数组,对一个字符串生成相应的后缀数组后,然后再排序,排完序依次检测相邻的两个字符串的开头公共部分。
这样的时间复杂度为:

生成后缀数组 O(N)
排序 O(NlogN*N) 最后面的 N 是因为字符串比较也是 O(N)
依次检测相邻的两个字符串 O(N * N)
总的时间复杂度是 O(N^2*logN), 


解答:

对于类似从给定的文本中,查找其中最长的重复子字符串的问题,可以采用“后缀数组”来高效地完成此任务。后缀数组使用文本本身和n个附加指针(与文本数组相应的指针数组)来表示输入文本中的n个字符的每个子字符串。
    首先,如果输入字符串存储在c[0..n-1]中,那么就可以使用类似于下面的代码比较每对子字符串:
    maxlen = -1
    for i = [0, n)
        for j = (i, n)
            if (thislen = comlen(&c[i], &c[j])) > maxlen
                maxlen = thislen
                maxi = i
                maxj = j
    当作为comlen函数参数的两个字符串长度相等时,该函数便返回这个长度值,从第一个字符开始:
    int comlen(char *p, char* q)
        i = 0
        while *p && (*p++ = *q++)
            i++
        return i
    由于该算法查看所有的字符串对,所以它的时间和n的平方成正比。下面便是使用“后缀数组”的解决办法。
    如果程序至多可以处理MAXN个字符,这些字符被存储在数组c中:
    #define MAXN 5000000
    char c[MAXN], *a[MAXN];
    在读取输入时,首先初始化a,这样,每个元素就都指向输入字符串中的相应字符:
    while (ch = getchar()) != EOF
  a[n] = &c[n];
  c[n++] = ch;
 c[n] = 0 //将数组c中的最后一个元素设为空字符,以终止所有字符串。
 这样,元素a[0]指向整个字符串,下一个元素指向以第二个字符开始的数组的后缀,等等。如若输入字符串为"banana",该数组将表示这些后缀:
 a[0]:banana
 a[1]:anana
 a[2]:nana
 a[3]:ana
 a[4]:na
 a[5]:a
 由于数组a中的指针分别指向字符串中的每个后缀,所以将数组a命名为"后缀数组"
 第二,对后缀数组进行快速排序,以将后缀相近的(变位词)子串集中在一起
 qsort(a, n, sizeof(char*), pstrcmp)后
 a[0]:a
 a[1]:ana
 a[2]:anana
 a[3]:banana
 a[4]:na
 a[5]:nana
 第三,使用以下comlen函数对数组进行扫描比较邻接元素,以找出最长重复的字符串:
 for i = [0, n)
     if comlen(a[i], a[i+1]) > maxlen
         maxlen = comlen(a[i], a[i+1])
         maxi = i
 printf("%.*s/n", maxlen, a[maxi])
 由于少了内层循环,只是多了一次排序,因此该算法的运行时间为O(n logn). 

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXCHAR 5000 //最长处理5000个字符

char c[MAXCHAR], *a[MAXCHAR];

int comlen( char *p, char *q ){
    int i = 0;
    while( *p && (*p++ == *q++) )
        ++i;
    return i;
}

int pstrcmp( const void *p1, const void *p2 ){
    return strcmp( *(char* const *)p1, *(char* const*)p2 );
}

int main(  ){
    char ch;
    int  n=0;
    int  i, temp;
    int  maxlen=0, maxi=0;
    printf("Please input your string:\n");
    while( (ch=getchar())!='\n' ){
        a[n]=&c[n];
        c[n++]=ch;
    }
    c[n]='\0';
    qsort( a, n, sizeof(char*), pstrcmp );
    for(i=0; i<n-1; ++i ){
        temp=comlen( a[i], a[i+1] );
        if( temp>maxlen ){
            maxlen=temp;
            maxi=i;
        }
    }
    printf("%.*s\n",maxlen, a[maxi]);
    system("PAUSE");
    return 0;




******************************************************************************
第三种解法:
******************************************************************************
用简单的两层循环,结合substring()方法就可以了

class Test  
{  
    String reg,left;  
    public String find(String str){  
        //最长的重复字串,极端情况就比如abcabc,最长重复字串就是abc  
        //即为字符串长度的一半,当然这是极端情况,通常都是小于串长一半的  
        for(int len=str.length()/2;len>0;len--){  
            //将字符串分隔为若干“最长字串”  
            for(int i=0;i
                //获取“最长字串”  
                reg=str.substring(0,len+1);  
                //刨去“最长字串”剩下的串  
                left=str.substring(len+1);  
                //如果剩下的串里面包含“最长字串”  
                if(left.indexOf(reg)!=-1)  
                    //bingo!  
                    return reg;  
            }  
        }  
        //啥也找不到就返回空吧  
        return null;  
    }  
    public static void main(String[] args) throws Exception{  
      
        String str=new Test().find("abcdabc");  
        System.out.println(str);  
    }  
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值