您的位置:首页 > 其它

动态规划 —— 最长公共子串(输出长度和第一个出现的子串)

2018-03-30 22:20 447 查看
1、求最长公共子串的长度
与上一篇博客中, 动态规划 —— 最长公共子序列 不同,公共子串是连续的。那么,在动态规划中,每一次碰到新的不同的两个字符时,计数器都要被置为 0.。还是以字符串 ABCBDAB 和 BDCABA 为例子,表格如下:

 ABCBDAB
B0101001
D0000200
C0010000
A1000010
B0201002
A1000010
分析:在填表格时,不再像最长公共子序列一样:如果两个字符相同,该位置为其左对角线的值 + 1,如果不同,则为 max{左边相邻的值,上面相邻的值}。因为公共子串要求连续,那么,
if (两个字符相同,即可a[i] == b[j])
{
      if (a[i-1] == b[j-1])
            该位置的值 = 其左对角线的值 + 1

      else
            该位置的值 = 1
}
利用状态转移方程表示就是:



代码如下:class LongestSubstring {
public:
int findLongest(string A, int n, string B, int m) {
// write code here

int dp[1000][1000] = {0};
int max_len = 0;
for(int i = 0;i < n;i++)
{
for(int j = 0;j < m;j++)
{
if(A[i] == B[j])
{
if(i >0 && j>0)
{
dp[i][j] = dp[i-1][j-1] +1;
}
else
{
dp[i][j] = 1; // i= j= 0 的时候,不能使用上面那个公式,所以要单独写出来
}

}
if(max_len < dp[i][j])
max_len = dp[i][j];
}
}
// cout << max_len ;
return max_len ;
}
};
2、求最长公共子串第一次出现在短的字符串中的子串
这个问题不再是求最长公共子串的长度,而是求具体的子串。难度又升级了一下:我们不仅要分析什么时候取最大的长度,还要确定是第一次出现。第一次最大长度的字符出现,我们要做一个标记,但是我们仍要继续遍历剩余的字符,只不过这个标定不再改变。
代码如下:void findMaxCommonStr(string s1, string s2)
{
if (s1.length()>s2.length())
swap(s1, s2);//s1用于保存较短的子串
int len1 = s1.length(), len2 = s2.length();
int maxLen = 0, start = 0;
vector<vector<int> >dp(len1 + 1, vector<int>(len2 + 1, 0));
for (int i = 1; i <= len1; ++i)
for (int j = 1; j <= len2; ++j)
{
if (s1[i - 1] == s2[j - 1])
{
dp[i][j] = dp[i - 1][j - 1] + 1;
if (dp[i][j]>maxLen) //此 if() 写在if (s1[i - 1] == s2[j - 1]) 里面,能保证是第一次出现的,并且保证取到最大长度
{
maxLen = dp[i][j];
start = i - maxLen;//记录最长公共子串的起始位置,并且记录的只有第一次出现 maxLen 的位置。因为如果以后出现相等的最大的,不会进行交换
}
}
}
cout << s1.substr(start, maxLen) << endl;
}
int main()
{
string s1, s2;
while (cin >> s1 >> s2)
{
findMaxCommonStr(s1, s2);
}
return 0;
}

还有另外一种方法,利用 STL 。string 中有一些非常好用的函数,参见我的另外一篇博客:string 的用法——进阶篇 利用 substr() 和 find() 函数,能较方便地解决这个问题:
代码如下:int main()
{
string a, b;
while (cin >> a >> b)
{
if (a.size() > b.size())
swap(a, b);//确保前面的一个字符串短;
string str_m;
for (int i = 0; i<a.size(); i++)
{
for (int j = i; j<a.size(); j++)
{
string temp = a.substr(i, j - i + 1);
if (int(b.find(temp))<0) // 确保是第一次找到,且是连续的。如果出现不一样的字符了,即未找到,就输出 -1
break;
else if (str_m.size()<temp.size()) // 保证找到的是最长的连续子串
str_m = temp;
}
}
cout << str_m << endl;
}
return 0;
}分析:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: