最长公共重复不重复子串系列问题
1.求一个字符串中连续出现的次数最多的子串
例如字符串“abababc”,最多连续出现的为ab,连续出现三次。
http://blog.csdn.net/ysu108/article/details/7795479
分析:
后缀数组arr[i= 0àstrlen(s) -1]
abababc
bababc
ababc
babc
abc
bc
c
可以看出第一个后缀数组和第三个后缀数组的起始都为ab,第5个后缀数组也为ab。可以看出规律来,一个字符串s,如果第一次出现在后缀数组i的前面,那么如果它重复出现,下一次出现应该在第i+len(s)个后缀数组的前面。
Eg:
intcon_sub(char *str, char**ret)
{
int max_time = 0;//连续出现的最多次数
int ret_len = 0;//连续出现的字符串的长度
char *addr = NULL;//连续出现字符串的起始地址
int len = strlen(str);
char **a = (char**)malloc(sizeof(char*)*len);
//生成后缀数组
for(inti=0; i<len; i++)
a[i] = &str[i];
//---重复字符串的长度---范围为到(len+1)/2
for(inti=1; i<=(len+1)/2; i++)
{
//当重复的字符串长度为i的时候,如果是连续出现的,那么第j和第j+i个后缀数组前面为重复的字符串
for(int j=0;j+i<=len-1;j++)
{
int k = j;
int temp_time = 1;
//key:第j和第j+i个后缀数组前面的i个字符组成的子串为重复的字符串
while(k+i <= len-1 && strncmp(a[k], a[k+i], i) == 0)
{
temp_time++;//重复次数
k += i;
}
if(temp_time > max_time)
{
max_time =temp_time;
ret_len = i;
addr = a[k];
}
}
}
*ret= new char[ret_len+1];
strncpy(*ret,addr, ret_len);
*(*ret+ret_len)= '\0';//返回重复的子字符串
return max_time;//返回重复次数
}
2.求一个字符串中的最长重复子串
还是上面的字符串,那么最长的重复子串为abab
http://blog.csdn.net/hackbuteer1/article/details/7968623
对一个字符串生成相应的后缀数组后,然后再排序,排完序依次检测相邻的两个字符串的开头公共部分,以便找出最长的重复子串。
Eg:
如若输入字符串为"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 ; i < n-1 ; ++i )
{
temp=comlen( a[i], a[i+1] );
if( temp>maxlen )
{
maxlen=temp;
maxi=i;
}
}
printf("%.*s\n",maxlen, a[maxi]);
int comlen( char *p, char *q )
{
int i = 0;
while( *p && (*p++ == *q++) )
++i;
return i;
}
3. 最长公共子串
http://blog.csdn.net/hackbuteer1/article/details/6686931
方法1:动态规划
方法2:将字符串s1和s2分别写在两把直尺上面(我依然用s1,s2来表示这两把直尺),然后将s1固定,s2的头部和s1的尾部对齐,然后逐渐移动直尺s2,比较重叠部分的字符串中的公共子串的长度,直到直尺s2的尾部移动到s1的头部。在这个过程中求得的最大长度就是s1、s2最大子串的长度。
eg:
int textquery(string query, string text)
{
inti,len_q,len_t;
intds,tmp,max_len = 0;
ints1begin,s2begin;
len_q= query.size();
len_t= text.size();
for(i= 0 ; i < len_q + len_t ; i++)
{
s1begin= s2begin = 0;
if(i< len_q)
s1begin= len_q - i;
else
s2begin= i - len_q;
tmp= 0;
ds= 0;
while(1)
{
if(s1begin>= len_q -ds || s2begin >= len_t-ds)
break;
if(query[s1begin+ds]!=text[s2begin+ds])
{
max_len= tmp>max_len?tmp:max_len;
tmp= 0;
}
else
tmp++;
ds++;
}
max_len= tmp>max_len?tmp:max_len;
}
returnmax_len;
}
4. 求字符串中最长无重复字符的子串
http://blog.csdn.net/luxiaoxun/article/details/8036544
对这个字符串构造后缀数组,在每个后缀数组中,寻找没有重复字符的最长前缀,最长的前缀就是要找的子串。
//得到字符串最长的无重复的前缀长度
int longestlen(char * p)
{
int hash[256];
int len = 0;
memset(hash,0,sizeof(hash));
while (*p &&!hash[*p])
{
hash[*p] = 1;
++ len;
++ p;
}
return len;
}
//使用后缀数组解法
int max_unique_substring4(char * str)
{
int maxlen = -1;
int begin = 0;
char *a[99999];
int n = 0;
while(*str != '\0')
{
a[n++] = str++;
}
for (int i=0; i<n; i++)
{
int temlen = longestlen(a[i]);
if (temlen > maxlen)
{
maxlen = temlen;
begin = i;
}
}
printf("%.*s\n", maxlen,a[begin]);
return maxlen;
}
5. 后缀数组--处理字符串的利器
http://blog.csdn.net/luxiaoxun/article/details/8041155
6.最长回文子串(最长对称子字符串)
这个算法思想其实很简单啊,时间复杂度为O(N2),空间复杂度仅为O(1)。就是对给定的字符串S,分别以该字符串S中的每一个字符C为中心,向两边扩展,记录下以字符C为中心的回文子串的长度。但是有一点需要注意的是,回文的情况可能是 a ba,也可能是 a b b a。
string expandAroundCenter(string s, int c1,int c2) {
intl = c1, r = c2;
intn = s.length();
while (l >= 0 && r <= n-1 && s[l] == s[r]) {
l--;
r++;
}
return s.substr(l+1,r-l-1);
}
string longestPalindromeSimple(string s) {
intn = s.length();
if(n == 0) return "";
string longest = s.substr(0, 1); // a single char itself is a palindrome
for(int i = 0; i < n-1; i++) {
string p1 = expandAroundCenter(s, i, i);
if (p1.length() > longest.length())
longest = p1;
string p2 = expandAroundCenter(s, i, i+1);
if (p2.length() > longest.length())
longest = p2;
}
return longest;