题目的主要信息:

查找两个字符串a,b中的最长公共子串的长度。

方法一:

枚举法。用maxLen暂存最长公共子串长度。枚举所有a中可能出现的子串,用find函数判断该子串是否在b中出现,如果在b中出现的话,比较该子串与已知的最长公共子串的长度,如果更长,则更新maxLen。枚举所有的子串用双重for循环,首先遍历一遍子串的起始位置,然后遍历一遍所有可能的长度。需要注意的是,当某个子串temp不在b中时,说明从这个位置开始,大于temp长度的子串都不可能在b中找到,因此可以结束当前循环,从下一个位置开始判断。枚举完所有子串后的maxLen就是最长公共子串长度。 alt

具体做法:

#include<iostream>
#include<string>
#include<math.h>

using namespace std;

int main(){
    string a,b;
    while(cin>>a>>b){//输入两个字符串
      int maxLen=0;
      for(int i=0;i<a.size();i++){
          for(int j=i;j<a.size();j++){
              string temp=a.substr(i,j-i+1);//temp为a的子串
              if(int(b.find(temp))<0){//若temp在b中没有出现,跳出当前循环,从下一个位置i开始找子串
                  break;
              }else if(maxLen<temp.size()){//找到了更长的公共子串
                  maxLen=temp.size();
              }
          }
      }
        cout<<maxLen<<endl;
    }
    return 0;
}


复杂度分析:

  • 时间复杂度:O(n2)O(n^2),两层for循环。
  • 空间复杂度:O(1)O(1),只用了常数空间。

方法二:

动态规划。动态数组dp[i][j]表示在b中以第i个字符结尾,a中以第j个字符结尾时的公共子串长度。,maxlen表示最长公共子串的长度。遍历一遍两个字符串:

  • 如果b中第i和a中第j个字符相同,则在以i-1和j-1结尾的子串后面加上i、j一位仍然是子串,因此dp[i][j]=dp[i−1][j−1]+1。
  • 如果b中第i和a中第j个字符不相同,则以他们结尾的子串不可能相同,dp[i][j]=0。

如果更新后的dp[i][j]比maxlen大,则更新最大子串信息。

具体做法:

#include <iostream>
#include <vector>

using namespace std;

int main(){
    string a,b;
    while (cin >> a >> b)
    {
        int maxLen = 0;
        vector<vector<int>> dp(b.size()+1,vector<int>(a.size()+1,0));//动态数组,dp[i][j]表示b以第i个字符结尾,a以第j个字符结尾的公共子串的长度
        for (int i = 1; i <= b.size(); ++i){
            for (int j = 1; j <= a.size(); ++j){
                if (b[i - 1] == a[j - 1]) {//b中第i个字符和a中第j个字符相同
                    dp[i][j] = dp[i - 1][j - 1] + 1;//前一个长度加一
                }else {
                    dp[i][j] = 0;//如果第i个字符和第j个字符不同,则以他们结尾的子串不可能相同
                }
                if (maxLen < dp[i][j]) {//更新最大值
                    maxLen = dp[i][j];
                }
            }
        }
        cout << maxLen << endl;
    }
    return 0;
}

复杂度分析:

  • 时间复杂度:O(mn)O(mn),两层for循环,遍历一遍dp数组。
  • 空间复杂度:O(mn)O(mn),dp数组大小为mn。