字符串-最长公共子串(两个,多个)

关于暴力解和动规说的最好的https://www.cnblogs.com/ider/p/longest-common-substring-problem-optimization.html

空间复杂度 O(1) 还是左神的p225说的好。其实也没什么意思orz!

问题:有两个字符串str1和str2,求出两个字符串中最长公共子串长度。

暴力解:

1)把str1和str2的所有子串都找到,然后挨个比较

时间复杂度:

假设两个字符串str1和str2的长度分别为x和y,则字符串的子串个数分别为

n1 = x + (x-1) + ... + 1 = x(x-1) / 2

n2 = y + (y-1) + ... + 1 = y(y-1) / 2

所以,暴力求解法下,对比两个子串是否相等,时间复杂度为O(x^2*y^2),即O(n^4)。

2)稍微把暴力解优化一点

  a)优化方法1

遍历str1的每一个子串a,然后str2.find(a)==string::npos 看str2里面有无对应的子串,因为find用的是kmp,find的复杂度是y,所以总的复杂度是O(x^2*y),即O(n^3)

  b)优化方法2

不用str.find 也能做到O(n^3)

不是遍历str1的每一个子串,而是把str1中以str1【i】为开头,str1【str1.len()-1】为结尾的子串,跟str2中以str2【j】为开头,str2【str2.len()-1】为结尾的子串,依次比较,找到两个字符串的最长公共前缀,复杂度依旧是O(n^3)。

3)动规 时间复杂度 O(n^2)

状态转移方程:

假设,两个字符串分别为

A = a1, a2, ..., ax

B = b1, b2, ..., by

我们定义dp[i][j]的含义是:必须把A[i-1]和B【j-1】当作公共子串最后一个字符的情况下,公共子串最长能有多长。
常规动规思想:https://blog.csdn.net/ten_sory/article/details/79857531

另一种角度:https://blog.csdn.net/qq_25800311/article/details/81607168

class LongestSubstring {
public:
    int findLongest(string A, int n, string B, int m) {
		if(n<=0 || m<=0) return 0;
        vector<vector<int>> dp(n+1,vector<int>(m+1,0));
		int res = 0;
		//dp【i】【j】 代表的是A[i-1] B[j-1]
		for(int i=1;i<=n;++i){
			for(int j=1;j<=m;++j){
				if(A[i-1]==B[j-1]){
					dp[i][j]=dp[i-1][j-1]+1;
					res = max(dp[i][j],res);
				}
			}
		}
		
		return res;
    }
};

 

假如问题是多个字符串呢?

自然dp数组的维度就是n维的,时间复杂度也是O(n^n),实际中自然不可能用这种方法的,那就是用后缀数组做,不过这逼玩意属实太难,假如面试碰到,跟他bb一下,就ok了

把n个字符串的所有后缀,排序,假如n个字符串的总长度是K,那么后缀的个数是K,而排序假如用倍增算法+基数排序,那时间复杂度是O(KlogK),即使用快排,假如字符串是随机的,那其实也就是O(KlogK)

最坏的情况: 快排每个后缀(n log n),但是这是字符串,所以比较任意两个后缀的复杂度其实是O(n),这样一来就是接近O(n^2 log n)的复杂度,但是其实,假如是随机的字符串,比较任意两个后缀的平均复杂度应该是O(1),因为一共就那么几个字符,比不了几个字符就判断出大小了。

然后还有几个重要的性质:

1)任意1个子串都是某个后缀的前缀

2)任意2个后缀(i,j)之间的最长公共前缀,都是这一段相邻后缀之间的最长公共前缀的最小值,

即  LCP(i,j)=min(i<k<=j)(LCP(k-1,k))

而求相邻两个最长公共前缀(即,里面的height数组)又有个优化,可以做到O(N)

 

搞出了这些之后,对于n个字符串的最长公共前缀,是后缀数组中相邻n项(得是分别来自n个不同字符串)的最大公共前缀,

最后的复杂度是 O(K* logK)

n个字符串的  具体的可以看看    https://www.xuebuyuan.com/3226411.html

后缀数组: 粗暴介绍 https://www.xuebuyuan.com/274781.html

                 性质2的证明 https://blog.csdn.net/qq_36172410/article/details/89816078

                 比较图形化的介绍: https://www.cnblogs.com/jinkun113/p/4743694.html

                                                  https://www.cnblogs.com/victorique/p/8480093.html

 最后贴的论文 很棒! https://blog.csdn.net/zy799894671/article/details/7761171

 

 

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 最长公共子串是指在两个字符串同时出现的最长的子串。可以使用动态规划的方法来解决这个问题。具体步骤如下: 1. 定义一个二维数组dp,其dp[i][j]表示以字符串a的第i个字符和字符串b的第j个字符结尾的最长公共子串的长度。 2. 初始化dp数组,将dp[i][j]的初始值设为。 3. 遍历字符串a和字符串b,如果a[i]等于b[j],则dp[i][j]的值为dp[i-1][j-1]+1,否则dp[i][j]的值为。 4. 在遍历的过程,记录最长公共子串的长度和起始位置,即dp[i][j]的值最大的位置。 5. 根据最长公共子串的长度和起始位置,可以得到最长公共子串。 6. 最终返回最长公共子串。 需要注意的是,如果最长公共子串多个,只返回其一个即可。 ### 回答2: 最长公共子串问题是指在两个字符串查找到最长的相同的子串,这个子串在两个字符串位置可以不同。这个问题是计算机科学经典的问题,有多种解法。 一种简单的解法是暴力枚举。首先找到两个字符串的所有子串(可以用双重循环),然后比较每一对子串是否相同,找到相同的最长子串。这种解法的时间复杂度是O(n^3),其n是字符串长度,效率比较低,适用于小数据量的字符串。 另一种解法是动态规划。定义一个二维数组dp,其dp[i][j]表示字符串a的前i个字符和字符串b的前j个字符的最长公共子串长度。初始化dp[i][j]=0,然后用双重循环遍历a和b的所有字符,如果a[i]==b[j],则dp[i][j]=dp[i-1][j-1]+1,表示在a的前i-1个字符和b的前j-1个字符的最长公共子串的基础上,加上这两个字符,可以得到a和b的前i个字符和前j个字符的最长公共子串。最后遍历dp数组,找到最大的dp[i][j],即为最长公共子串。这种解法的时间复杂度是O(n^2),效率较高,适用于等数据量的字符串。 还有一种解法是基于后缀数组的。后缀数组是指对于一个字符串S,将它的所有后缀按照字典序排序后存储的数组,可以用于查找字符串的子串。具体做法是将两个字符串a和b拼接成一个新的字符串S,然后求出S的后缀数组sa,然后求出sa相邻两个后缀的最长公共前缀长度,最长的就是a和b的最长公共子串。这种解法的时间复杂度是O(nlogn),效率比较高,适用于大数据量的字符串。 综上所述,查找两个字符串最长公共子串有多种解法,每种解法的适用情况不同,需要根据实际情况选择合适的算法。 ### 回答3: 最长公共子串问题是计算机领域的一个经典问题,旨在寻找两个字符串相同的最长子字符串。解决该问题对于文本比较和字符串匹配等应用非常有用。 解决这个问题的方法有很多种,但最常见的方法是使用动态规划算法。具体步骤如下: 1. 创建一个二维数组dp,其dp[i][j]表示字符串a的前i个字符和字符串b的前j个字符之间的最长公共子串的长度。 2. 初始化dp数组的第一行和第一列为0。 3. 通过遍历两个字符串的每个字符来填充dp数组。对于i和j,如果a[i-1]等于b[j-1],则dp[i][j]等于dp[i-1][j-1]加1;否则,dp[i][j]为0。 4. 在dp数组找到最大值,这个最大值就是两个字符串最长公共子串的长度。 5. 找到最长公共子串的方法是定位dp数组最大值所在的位置i和j,然后从a[i-dp[i][j]]到a[i-1]或从b[j-dp[i][j]]到b[j-1]这个子串就是所要的答案。 以上就是解决查找两个字符串a,b最长公共子串的算法,可以在程序设计进行应用,达到处理文本以及字符串匹配等操作的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值