最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和

首先这是一个单字符串问题。子字符串R 在字符串L 中至少出现两次,则称R 是L 的重复子串。重复子串又分为可重叠重复子串和不可重叠重复子串,这里只是简单讨论最长可重叠的重复子串,给出基本算法和基于后缀数组的算法;关于后缀数组,这里也只是用最简单的形式实现,对于后缀数组的倍增算法和DC3算法的实现以及不可重叠重复子串的问题可参见算法合集之《后缀数组——处理字符串的有力工具》,以后再整理这几个问题。

最直接的方法就是子串和子串间相互比较,这样查看所有的子串对,时间复杂度为O(n^2),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* 最长重复子串 Longest Repeat Substring */
 
int maxlen;    /* 记录最长重复子串长度 */
int maxindex;  /* 记录最长重复子串的起始位置 */
void outputLRS(char * arr);  /* 输出LRS */
 
/* 最长重复子串 基本算法 */
int comlen(char * p, char * q)
{
     int len = 0;
     while(*p && *q && *p++ == *q++)
     {
         ++len;
     }
     return len;
}
 
void LRS_base(char * arr, int size)
{
     for(int i = 0; i < size; ++i)
     {
         for(int j = i+1; j < size; ++j)
         {
             int len = comlen(&arr[i],&arr[j]);
             if(len > maxlen)
             {
                 maxlen = len;
                 maxindex = i;
             }
         }
     }
     outputLRS(arr);
}

第二种方法便是采用《编程珠玑》上介绍的“后缀数组”,这个结构是一个字符指针数组,记录目标字符串的所有后缀的起始地址,例如banana这个单词的后缀数组为:

1
2
3
4
5
6
7
8
9
10
11
suff[0]:banana
 
suff[1]:anana
 
suff[2]:nana
 
suff[3]:ana
 
suff[4]: na
 
suff[5]: a

如果某个子串在目标字符串中出现两次,那么它必将出现在两个不同的后缀中,因此对后缀数组进行排序,以寻找相同的后缀,然后扫描数组,比较相邻的元素便可以找出最长的重复子串。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* 最长重复子串 后缀数组 */
char * suff[30];
 
int pstrcmp(const void * p, const void * q)
{
     return strcmp(*(char**)p, *(char**)q);
}
 
void LRS_suffix(char * arr, int size)
{
     int suff_index = maxlen = maxindex = 0;
 
     for(int i = 0; i < size; ++i) /* 初始化后缀数组 */
     {
         suff[i] = & arr[i];
     }
     qsort(suff, size, sizeof(char *), pstrcmp); /* 排序后缀 */
     for(int i = 0; i < size-1; ++i)  /* 寻找最长重复子串 */
     {
         int len = comlen(suff[i],suff[i+1]);
         if(len > maxlen)
         {
             maxlen = len;
             suff_index = i;
         }
     }
     outputLRS(suff[suff_index]);
}

最后给出输出程序和测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/* 输出LRS
  * 在后缀数组方法中,maxindex=0
  * 因为传进来的就是后缀数组suff[],从0打印maxlen个字符
*/
void outputLRS(char * arr)
{
     if(maxlen == 0)
     {
         printf("NULL LRS\n");
         return;
     }
     printf("The len of LRS is %d\n",maxlen);
 
     int i = maxindex;
     while(maxlen--)
     {
         printf("%c",arr[i++]);
     }
     printf("\n");
}
 
void main()
{
     char X[] = "banana";
 
     /* 基本算法 */
     LRS_base(X,strlen(X));
 
     /* 后缀数组方法 */
     LRS_suffix(X,strlen(X));
}
文章的其余内容 点击打开链接
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
公共序列(Longest Common Subsequence, LCS)和最公共(Longest Common Substring)是两个常见的字符相关问题。 最公共序列是指给定两个字符,要求找到它们之间最的公共序列度。序列是从原字符中删除若干个字符而得到的新字符,字符在新字符中的相对顺序与原字符中的保持一致。动态规划是求解LCS问题的常用方法。 以字符s1 = "ABCBDAB"和s2 = "BDCAB"为例,可以使用动态规划的方法求解最公共序列度。首先创建一个二维数组dp,dp[i][j]表示s1的前i个字符和s2的前j个字符之间的最公共序列度,那么有以下推导关系: 1. 当i=0或j=0时,dp[i][j]=0。 2. 当s1[i-1]=s2[j-1]时,dp[i][j] = dp[i-1][j-1] + 1。 3. 当s1[i-1]!=s2[j-1]时,dp[i][j] = max(dp[i-1][j], dp[i][j-1])。 最后,dp[len(s1)][len(s2)]即为最公共序列度。 对于最公共,要求找到两个字符中最的公共连续度。连续是指在原字符中连续出现的字符序列。同样可以使用动态规划来解决该问题。 仍以上述两个字符s1和s2为例,创建一个二维数组dp,dp[i][j]表示以s1[i-1]和s2[j-1]为结尾的公共度,那么有以下推导关系: 1. 当i=0或j=0时,dp[i][j]=0。 2. 当s1[i-1]=s2[j-1]时,dp[i][j] = dp[i-1][j-1] + 1。 3. 当s1[i-1]!=s2[j-1]时,dp[i][j] = 0。 最后,dp矩阵中的最大值即为最公共度。 以上就是求解最公共序列和最公共的常见方法。在实际应用中,我们可以根据具体的问题选择合适的方法,并结合动态规划来解决这些字符相关的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值