最长递归子序列、最长公共字串、最长公共子序列、编辑距离

[TOC]  

### 最长递归子序列

#### 题目

给定数组arr,返回arr中的最长递增子序列,如`arr=[2,1,5,3,6,4,8,9,7]`,返回的最长递增子序列为`[1,3,4,8,9]`

#### 题解思路

先用DP来求解子序列递增的最大长度,如arr的长度序列为`dp=[1,1,2,2,3,3,4,5,4]`,然后对这个长度序列dp从右到左遍历,得到最长递增子序列。

1. 求解长度序列,令dp[i]表示在以arr[i]这个数结尾的情况下,arr[0...i]中的最大递增子序列的长度。

则状态转移方程为:`dp[i]=max{dp[j]+1(0<=j<i,arr[j]<arr[i])}`

2. 然后遍历dp数组,从右向左,如`dp=[1,1,2,2,3,3,4,5,4]`,先找到最大值为5,索引为7,即‘9‘,前一个数则应该是4,索引为6,即‘8‘;重复该步骤即可;

该方法时间复杂度为O(N^2)

如果在求解dp数组的过程中采用二分查找来进行优化,可以将复杂度降低到O(N*logN)

```

class LIS

{

public:

LIS(vector<int> _nums) :nums(_nums){}

//获得最长递增子序列

vector<int> get_lis()

{

vector<int> len_dp = get_length();

vector<int> ret = solve_lis(len_dp);

return ret;

}

private:

vector<int> nums;

//dp求解长度

vector<int> get_length()

{

//状态转移方程为:dp[i]=max{dp[j]+1(0<=j<i,arr[j]<arr[i])}

vector<int> ret(nums.size(), 1);

for (int i = 0; i < nums.size(); i++)

{

for (int j = 0; j < i; j++)

{

if (nums[j] < nums[i])

ret[i] = max(ret[i], ret[j] + 1);

}

}

return ret;

}

//从右向左获取最长递增子序列

vector<int> solve_lis(vector<int> len_dp)

{

int len = 0;

int index = 0;

//先找到最大长度和索引

for (int i = 0; i < len_dp.size(); i++)

{

if (len_dp[i] > len)

{

len = len_dp[i];

index = i;

}

}

//从右向左遍历

vector<int> ret(len, 0);

ret[--len] = nums[index];

for (int i = index; i >= 0; i--)

{

if (nums[i] < nums[index] && len_dp[i] == len_dp[index] - 1)

{

ret[--len] = nums[i];

index = i;

}

}

return ret;

}

};

```

### 最长公共字串

#### 题目

给定两个字符串str1和str2,返回两个字符串的最长公共字串,如str1=‘1AB2345CD‘,str2=‘12345EF‘,返回‘2345‘

#### 解题思路

经典的动态规划方法的时间复杂度为`O(M*N)`,空间复杂度为`O(M*N)`,采用压缩优化后,可将空间复杂度优化至`O(1)  `

dp[i][j]表示将str1[i]和str2[j]作为公共字串的最后一个字符,公共字串的长度。则:

状态转移方程为:`dp[i+1,j+1]=(str1[i]==str2[j])?dp[i,j]+1:0;`

```

//最长公共字串,经典DP问题

string longestCommonSubstring(const string& str1, const string& str2)

{

size_t size1 = str1.size();

size_t size2 = str2.size();

if (size1 == 0 || size2 == 0) return 0;

vector<vector<int> > dp(size1, vector<int>(size2, 0));

// 初始化

for (int i = 0; i < size1; ++i)

{

dp[i][0] = (str1[i] == str2[0] ? 1 : 0);

}

for (int j = 0; j < size2; ++j)

{

dp[0][j] = (str1[0] == str2[j] ? 1 : 0);

}

//dp

for (int i = 1; i < size1; ++i)

{

for (int j = 1; j < size2; ++j)

{

if (str1[i] == str2[j])

{

dp[i][j] = dp[i - 1][j - 1] + 1;

}

}

}

//找字串,max为长度,end_ix表示字串结束位置

int max = 0;

int end_idx = 0;

for (int i = 0; i < size1; ++i)

{

for (int j = 0; j < size2; ++j)

{

if (max < dp[i][j])

{

max = dp[i][j];

end_idx = i;

}

}

}

//获取字串并返回

return str1.substr(end_idx - max + 1, max);

}

```

优化思路:因为计算每个dp[i][j]时只用到了其左上方的dp[i-1][j-1],所以只需要一个变量即可,即空间复杂度为O(1)

### 最长公共子序列

#### 题目

给定两个字符串str1和str2,返回两个字符串的最长公共子序列

#### 思路

状态转移方式:

c[i][j]记录str1[i]与str2[j] 的LCS 的长度

```

//最长公共子序列,时间辅助度和空间辅助度都是O(M*N)

string get_lcs_subsequece(string str1, string str2)

{

vector<vector<int>> dp(str1.size(), vector<int>(str2.size(), 0));

dp[0][0] = str1[0] == str2[0] ? 1 : 0;

for (int i = 1; i < str1.length(); i++)

{

dp[i][0] = max(dp[i - 1][0], str1[i] == str2[0] ? 1 : 0);

}

for (int j = 1; j < str2.length(); j++)

{

dp[0][j] = max(dp[0][j - 1], str1[0] == str2[j] ? 1 : 0);

}

for (int i = 1; i < str1.length(); i++)

{

for (int j = 1; j < str2.length(); j++)

{

dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);

if (str1[i] == str2[j])

{

dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);

}

}

}

//获取子序列

int m = str1.size()-1;

int n = str2.size()-1;

string res;

int index = dp[m][n]-1;

while (index >= 0)

{

if (n > 0 && dp[m][n] == dp[m][n - 1])

{

n--;

}

else if (m > 0 && dp[m][n] == dp[m - 1][n])

{

m--;

}

else

{

res.insert(res.begin(), str1[m]);

index--;

/*res[index--] = str1[m];*/

m--;

n--;

}

}

return res;

}

```

### 编辑距离

#### 题目

传送[leetcode Edit Distance](https://leetcode.com/problems/edit-distance/)

#### 解题思路:

设状态为 f[i][j],表示 A[0,i] 和 B[0,j] 之间的最小编辑距离。设 A[0,i] 的形式是 str1c,B[0,j] 的形式是 str2d,

1. 如果 c==d,则 f[i][j]=f[i-1][j-1];

2. 如果 c!=d,

(a) 如果将 c 替换成 d,则 f[i][j]=f[i-1][j-1]+1;

(b) 如果在 c 后面添加一个 d,则 f[i][j]=f[i][j-1]+1;

(c) 如果将 c 删除,则 f[i][j]=f[i-1][j]+1;

```

// LeetCode, Edit Distance

// 二维动规,时间复杂度 O(n*m),空间复杂度 O(n*m)/*Edit Distance*/

int minDistance(string word1, string word2)

{

if (word1 == "" && word2=="") return 0;

vector<vector<int>> dis(word1.size()+1, vector<int>(word2.size()+1, 0));

for (size_t i = 0; i <= word1.size(); i++)

{

dis[i][0] = i;

}

for (size_t i = 0; i <= word2.size(); i++)

{

dis[0][i] = i;

}

for (size_t i = 1; i <= word1.size(); i++)

{

for (size_t j = 1; j <= word2.size(); j++)

{

if (word1[i - 1] == word2[j - 1])

dis[i][j] = dis[i - 1][j - 1];

else

{

int mn = min(dis[i - 1][j], dis[i][j - 1]);

dis[i][j] = 1 + min(dis[i - 1][j - 1], mn);

}

}

}

return dis[word1.size()][word2.size()];

}

```

### 字符串的交错组成

#### 题目

给定三个字符串s1,s2,s3,判断s3是否由s1和s2交错组成。leetcode题目:`[Interleaving String](https://leetcode.com/problems/interleaving-string/)`

#### 分析

设状态 f[i][j],表示 s1[0,i] 和 s2[0,j],匹配 s3[0, i+j]。如果 s1 的最后一个字符等

于 s3 的最后一个字符,则` f[i][j]=f[i-1][j]`;如果 s2 的最后一个字符等于 s3 的最后一个字符,则 f[i][j]=f[i][j-1]。

因此状态转移方程如下:

`f[i][j] = (s1[i - 1] == s3 [i + j - 1] && f[i - 1][j]) || (s2[j - 1] == s3 [i + j - 1] && f[i][j - 1]);`

二维DP,时间复杂度 O(n^2),空间复杂度 O(n^2) ,另外可以用滚动数组进行空间复杂度优化,O(N)

```

class Solution

{

public:

bool isInterleave(string s1, string s2, string s3)

{

if (s3.length() != s1.length() + s2.length())

return false;

vector<vector<bool>> f(s1.length() + 1,

vector<bool>(s2.length() + 1, true));

for (size_t i = 1; i <= s1.length(); ++i)

f[i][0] = f[i - 1][0] && s1[i - 1] == s3[i - 1];

for (size_t i = 1; i <= s2.length(); ++i)

f[0][i] = f[0][i - 1] && s2[i - 1] == s3[i - 1];

for (size_t i = 1; i <= s1.length(); ++i)

for (size_t j = 1; j <= s2.length(); ++j)

f[i][j] = (s1[i - 1] == s3[i + j - 1] && f[i - 1][j])

|| (s2[j - 1] == s3[i + j - 1] && f[i][j - 1]);

return f[s1.length()][s2.length()];

}

};

```

来自为知笔记(Wiz)

时间: 2024-12-25 05:51:25

最长递归子序列、最长公共字串、最长公共子序列、编辑距离的相关文章

最长公共子序列与最长公共字串

显然最长公共子序列不一定需要连续的,只要字符的顺序严格递增即可.最长公共字串需要字符连续 子序列代码: package test; import java.util.*; /* * 本题是求最长公共子序列,子序列未必连续,只需要严格递增即可 * 如 abcdeeeeeeeee和atttbggcd 最长公共子序列为abcd 长度为4 * * */ public class Main4{ public static void main(String... args){ try(Scanner in

(字符串)最长公共字串(Longest-Common-SubString,LCS)

题目: 给定两个字符串X,Y,求二者最长的公共子串,例如X=[aaaba],Y=[abaa].二者的最长公共子串为[aba],长度为3. 子序列是不要求连续的,字串必须是连续的. 思路: 1.简单思想: 遍历两个字符串X.Y,分别比较X的字串与Y的字串,求出最长的公共字串. #include <iostream> #include <vector> using namespace std; int getComLen(char *str1,char *str2){ int len=

【编程题目】最长公共字串

56.最长公共字串(算法.字符串).题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串.注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中.请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串.例如:输入两个字符串 BDCABA 和 ABCBDAB,字符串 BCBA 和 BDAB 都是是它们的最长公共子串,则输出它们的长度 4,并打印任意一个子串. 经典动态规划题. #include <stdio.h> #i

搜索里的相似度计算-最长公共字串

相似度计算的任务是根据两段输入文本的相似度返回从0到1之间的相似度值:完全不相似,则返回0,:完全相同,返回1.衡量两端文字距离的常用方法有:海明距离(Hamming distance),编辑距离,欧氏距离,文档向量的夹角余弦距离,最长公共字串. 1. 余弦相似度 把两篇文档看作是词的向量,如果x,y为两篇文档的向量,则:Cos(x, y) = (x * y) / (||x|| * ||y||) 使用Hashmap可以很方便的把这个计算出来 2. 最长公共字串(Longest Common Su

URAL 1517 Freedom of Choice(后缀数组,最长公共字串)

题目 输出最长公共字串 #define maxn 200010 int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; int cmp(int *r,int a,int b,int l) {return r[a]==r[b]&&r[a+l]==r[b+l];}//yuan lai zhi qian ba zhe li de l cuo dang cheng 1 le ... void da(int *r,int *sa,int n,int m) { int i,j

华为OJ——公共字串计算

题目描述 题目标题: 计算两个字符串的最大公共字串的长度,字符不区分大小写 详细描述: 接口说明 原型: int getCommonStrLength(char * pFirstStr, char * pSecondStr); 输入参数: char * pFirstStr //第一个字符串 char * pSecondStr//第二个字符串 输入描述: 输入两个字符串 输出描述: 输出一个整数 输入例子: asdfas werasdfaswer 输出例子: 6 import java.util.

公共字串计算——String.regionMatches方法 &amp; Java标签

题目:输入两个字符串,计算两个字符串的最大公共字串的长度,并输出,字符不区分大小写 eg:输入abcde  xxxBcyyy,输出 2. 完整Java代码: import java.util.*; public class Main { public static void main(String arg[]){ Scanner s=new Scanner(System.in); String str1=s.next(); String str2=s.next(); s.close(); Str

华为OJ: 公共字串计算

有几个需要注意的地方,一个这道题是不区分大小写的,所以在计算之前对输入的字符串要做小写或者大写的转换. 第二个,思路一定要清晰,先将s1从[i]处开始与s2的[j]开始匹配,不相等则j++直到j等于s2.length()-1,相等,则i++,j++.注意,这里就是i++,即下次重新开始从s[i]开始匹配时,两次i之间的距离可能会超过1.再j那里设置一个计数器计数即可. import java.util.Scanner; public class findMaxSubStringLength {

华为机试-公共字串计算

题目描述题目标题:计算两个字符串的最大公共字串的长度,字符不区分大小写详细描述:接口说明原型:int getCommonStrLength(char * pFirstStr, char * pSecondStr);输入参数: char * pFirstStr //第一个字符串 char * pSecondStr//第二个字符串 输入描述:输入两个字符串 输出描述:输出一个整数 输入例子:asdfas werasdfaswer 输出例子:6 效率低的Java程序实现: import java.ut