字符串专题

1 替换空格

链接:https://www.nowcoder.com/profile/351750300/codeBookDetail?submissionId=80854741

题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

注意点:字符串以’\0’结尾

class Solution {
public:
	void replaceSpace(char *str,int length) {
        if(str == NULL)
            return;

        //计算替换后字符串长度
        int new_len = 0;
        int init_len = 0;
        int i;
        for(i = 0; str[i] != '\0'; i++){
            if(str[i] == ' ')
                new_len += 3;
            else
                new_len += 1;
            init_len += 1;
        }

        //从后向前替换空格
        //一定一定要注意,字符串以'\0'结尾,所以遍历的最后一个就是len,而不是len-1
        int j = new_len;
        for(i = init_len; i >= 0 && j > i; i--){
            if(str[i] == ' '){
                str[j--] = '0';
                str[j--] = '2';
                str[j--] = '%';
            }
            else{
                str[j--] = str[i];
            }
        }
	}
};

2 第一次只出现一次的字符

【链接】
https://www.nowcoder.com/profile/351750300/codeBookDetail?submissionId=80858118

【题目描述】
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)

【注意点】

  • string s,s[i]为char类型
  • 了解STL string的函数
class Solution {
public:
    int FirstNotRepeatingChar(string str) {
        map<char, int> mp;

        for(int i = 0; i < str.size(); ++i)
            mp[str[i]]++;

        for(int i = 0; i < str.size(); ++i){
            if(mp[str[i]] == 1)
                return i;
        }
        return -1;
    }
};

3 最长不重复子串

【链接】
https://www.nowcoder.com/profile/351750300/codeBookDetail?submissionId=80868091

【题目描述】
给定一个字符串,找出最长的不具有重复字符的子串的长度。例如,“abcabcbb”不具有重复字符的最长子串是“abc”,长度为3。对于“bbbbb”,最长的不具有重复字符的子串是“b”,长度为1。

【O(n^2)解法】

  • 类似最长递增序列解法
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s.size() <= 1)
            return s.size();
        
        int max_len = 0;
        int cur_len;
        map<char,int> mp; //初始值为0 //记录次数
        for(int i = 1; i < s.size(); i++){
            mp[s[i]] = 1;
            cur_len = 1;
            for(int j = i-1; j >= 0; j--){
                //存在重复
                if(mp[s[j]] == 1)
                    break;
                cur_len += 1;
                mp[s[j]] += 1;
            }
            max_len = max(max_len, cur_len);
            mp.clear();         
        }
        
        return max_len;
    }
};

【O(n)解法】

O(n^2)的过程存在重复计算

思路:
“滑动窗口”
比方说 abcabccc 当你右边扫描到abca的时候你得把第一个a删掉得到bca,
然后"窗口"继续向右滑动,每当加到一个新char的时候,左边检查有无重复的char,
然后如果没有重复的就正常添加,
有重复的话就左边扔掉一部分(从最左到重复char这段扔掉),在这个过程中记录最大窗口长度

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        map<char,int> book;
        int i, max_len = 0, left = -1;
        for(i=0; i<s.size(); i++)
            book[s[i]] = -1;
        
        for(i = 0; i < s.size(); i++){
            left = max(left, book[s[i]]); //pre、book初始值为-1,当book>pre时,说明存在重复。 pre = book[s[i]],相当于将book[s[i]]左侧数据删除
            max_len = max(max_len, i - left);
            book[s[i]] = i; //记录对应下标
        }
        return max_len;
    }
};

4 找到子串在母串首次出现位置

【链接】
https://www.nowcoder.com/profile/351750300/codeBookDetail?submissionId=80951632

【题目描述】
实现函数 strStr。
函数声明如下:
char *strStr(char *haystack, char *needle)
返回一个指针,指向needle第一次在haystack中出现的位置,如果needle不是haystack的子串,则返回null。

【思路】

  • 遍历判断即可
  • 注意char数组字符串以’\0’结尾
class Solution {
public:
    char *strStr(char *haystack, char *needle) {
        if(*needle == '\0')
            return haystack;

        int src_idx, dst_idx;
        for(int i = 0; haystack[i] != '\0';i++){
            if(haystack[i] == needle[0]){
                src_idx = i;
                dst_idx = 0;
                while(haystack[src_idx] != '\0' && needle[dst_idx] != '\0' && haystack[src_idx] == needle[dst_idx]){
                    src_idx++;
                    dst_idx++;
                }
                if(needle[dst_idx] == '\0')
                    return haystack + i;
            }
        }
        return NULL;
    }
};

5 大整数乘法

【链接】
https://www.nowcoder.com/profile/351750300/codeBookDetail?submissionId=80961172

【题目描述】
给出两个用字符串表示的数字,将两个数字的乘积作为字符串返回。
备注:数字可以无限大,且是非负数。

【注意点】

  • char转int、int转char
  • 长度为x1的数乘以长度为x2的数,最终结果长度不超过x1+x2
  • 顺序翻转
  • 模拟乘法计算
class Solution {
public:
    string multiply(string num1, string num2) {
        if(num1.empty() || num2.empty())
            return "";

        //便于字符串计算
        reverse(num1.begin(), num1.end());
        reverse(num2.begin(), num2.end());

        //长度为x1的数乘以长度为x2的数,最终结果长度不超过x1+x2
        //初始值为0
        vector<int> val(num1.size() + num2.size()); 

        //按照乘法模拟计算
        int i, j;
        for(i = 0; i < num1.size(); i++){
            int carry = 0;
            for(j = 0; j < num2.size(); j++){
                int tmp = (val[i+j] + carry + (num1[i] - '0') * (num2[j] - '0')); //字符串转数字 char a = '0'; a - '0' = 0;
                val[i+j] = tmp % 10;
                carry = tmp / 10;
            }
            if(carry > 0)
                val[i+j] = carry;
        }

        
        //找到最终结果的第一位数所在位置
        //注意边界为>0,因为结果至少一位数
        int idx;
        for(idx = val.size()-1; idx > 0; idx--)
            if(val[idx] != 0)
                break;
        
        //从前后向填充string:res
        string res = "";
        while(idx >= 0){
            res.push_back(val[idx]+'0'); //注意将index转成char
            idx--;
        }
        return res;
    }
};

6 母串S中统计子串T个数(动态规划)

【链接】
https://www.nowcoder.com/profile/351750300/codeBookDetail?submissionId=80931601

【题目描述】
给定两个字符串S和T,返回S子序列等于T的不同子序列个数有多少个?
(这个信息很重要)字符串的子序列是由原来的字符串删除一些字符(也可以不删除)在不改变相对位置的情况下的剩余字符(例如,"ACE"is a subsequence of"ABCDE"但是"AEC"不是)
例如:
S =“rabbbit”, T =“rabbit”
返回3

【思路一】

  • 类似组合排列,DFS
  • 测试用例通过50%,但是超时
class Solution {
public:
    void findStr(string S, string T, string& sub_str, int start, int& num){ 
        if(sub_str == T){
            num = num + 1;
            return;
        }
        else{
            for(int i = start; i < S.size(); i++){
                sub_str.push_back(S[i]);
                findStr(S, T, sub_str, i+1, num);
                sub_str.pop_back();
            }
        }
    }
    
    int numDistinct(string S, string T) {
        if(S.size() == 0)
            return 0;
        
        int num = 0;
        string sub_str = "";
        findStr(S, T, sub_str, 0, num);
        return num;
    }
};

【思路二】动态规划,类似最长递增子序列的思路

  • 状态定义:dp[i][j]代表母串S[0i-1]中子串T[0j-1]不同子串的个数(出现次数)。

  • 递推关系式:

    • S[i-1]!= T[j-1]: DP[i][j] = DP[i][j-1] (不选择S中的s[i-1]字符)
    • S[i-1]==T[j-1]: DP[i][j] = DP[i-1][j-1](选择S中的s[i-1]字符) + DP[i][j-1]
  • 初始状态:第0列:DP[i][0] = 0;第0行:DP[0][j] = 1;DP[0][0] = 1,当母串子串都是0长度时,次数是1(因为都是空,相等)。

【详细解释】

我们需要一个二维数组dp(i)(j)来记录长度为i的母串(下标从1到i)中长度为j的子串(下标从1到j)中出现的次数。(为什么是1开始,因为第0行、0列为空串初始化)

  • 首先我们先初始化矩阵,当子串长度为0时,所有次数都是1,当母串长度为0时,所有次数都是0.当母串子串都是0长度时,次数是1(因为都是空,相等)。

  • 接着,如果子串的最后一个字母和母串的最后一个字母不同,说明新加的母串字母没有产生新的可能性,可以沿用该子串在较短母串的出现次数,所以dp(i)(j) = dp(i-1)(j)

  • 如果子串的最后一个字母和母串的最后一个字母相同,说明新加的母串字母带来了新的可能性,我们不仅算上dp(i-1)(j),也要算上新的可能性。

  • 那么如何计算新的可能性呢,其实就是在既没有最后这个母串字母也没有最后这个子串字母时,子串出现的次数,我们相当于为所有这些可能性都添加一个新的可能。所以,这时dp(i)(j) = dp(i-1)(j) + dp(i-1)(j-1)。

总结:图直观理解(二维矩阵,行为长度递增母串,列为长度递增子串),遍历先保持母串长度相同,递增子串长度,当母串与子串末尾不一样时,元素值实际是上方数字,当一样时,就是左上方数字 + 上方数字

在这里插入图片描述

class Solution {
public:
    int numDistinct(string S, string T) {
        int s_len = S.length();
        int t_len = T.length();

        //行为母串,列为子串
        //注意+1,初始行列考虑为空的情况
        vector<vector<int>> dp(s_len+1,vector<int>(t_len+1));

        int i,j; //i为行下标,j为列下标

        //初始化第一行,母串S为"",除了子串T为""的情况,结果全部为0
        for (j = 1; j < t_len + 1; j++) 
            dp[0][j] = 0;

        //初始化第一列,子串T为"",结果全部为1
        for (i = 0; i < s_len + 1; i++)
            dp[i][0] = 1;

        //固定母串,不断看子串
        for (int i = 1; i < s_len + 1; i++) {
            for (int j = 1; j < t_len + 1; j++) {
                if (S[i-1] == T[j-1])
                    dp[i][j] =dp[i - 1][j] + dp[i - 1][j - 1];
                else
                    dp[i][j] = dp[i - 1][j];
            }
        }
        return dp[s_len][t_len];
    }
};

7 判断字符串S3是否由S1、S2组成(动态规划)

【链接】
https://www.nowcoder.com/profile/351750300/codeBookDetail?submissionId=80946725

【题目描述】
给出三个字符串s1, s2, s3,判断s3是否可以由s1和s2交织而成。
例如:
给定
s1 =“aabcc”,
s2 =“dbbca”,
如果s3 =“aadbbcbcac”, 返回true
如果s3 =“aadbbbaccc”, 返回false

【思路一】pass
类似合并有序序列方案。不然更新下标,从后向前判断对应字符。通过率85%,【bad case】:s1 = “cca”,s2 = “baa”, s3 = “baacca”。如果移动路径不对(当s1[i] == s3[k] && s2[j] == s3[k]时,无法判定移动哪条路径),则会出错

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int i = s1.length();
        int j = s2.length();
        int k = s3.length();

        while(k > 0 || i > 0 || j > 0){
            if(s1[i-1] == s3[k-1]){
                i--;
                k--;
            }
            else if(s2[j-1] == s3[k-1]){
                j--;
                k--;
            }
            else
                break;
        }

        if(i == 0 && j == 0 && k == 0)
            return true;

        return false;
    }
};

【思路二】动态规划
s3是由s1和s2交织生成的,意味着s3由s1和s2组成,在s3中s1和s2字符的顺序是不能变化的,和子序列题型类似,这种题我们一般是用动态规划来解。

设dp[i][j]表示s3的前i+j个字符可以由s1的前i个字符和s2的前j个字符交织而成

状态转移方程:有两种情况

  • 第一个状态转移方程:
    dp[i][j]= {(dp[i - 1][j] && s1[i - 1] == s3[i + j - 1]}
    dp[i-1][j]表示若s3的前i+j-1个字符能够由s1前i-1个字符和s2的前j个字符交织而成,那么只需要s1的第i个字符与s3的第i+j个字符相等(索引从0开始),那么dp[i][j]=true;
  • 第二个状态转移方程:
    dp[i][j]= {(dp[i][j-1] && s2[j - 1] == s3[i + j - 1]}
    dp[i-1][j]表示若s3的前i+j-1个字符能够由s1前i个字符和s2的前j-1个字符交织而成,那么只需要s2的第j个字符与s3的第i+j个字符相等(索引从0开始),那么dp[i][j]=true;

如何想更好理解过程,可以画一个二维矩阵图,拿思路一的badcase计算一遍

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int size1 = s1.size();
        int size2 = s2.size();
        int size3 = s3.size();

        if(size1 + size2 != size3)
        	return false;

        vector<vector<bool>> dp(size1+1, vector<bool> (size2+1));
        dp[0][0]=true;

        //dp[i][j]表示的是S1的前(0..i-1)个与s2的前j个能否匹配(0...j-1)S3

        //行、列初始化
        for(int i = 1; i <= size1; i++)
          dp[i][0] = dp[i-1][0] && (s1[i-1] == s3[i-1]);
        for(int i = 1; i <= size2; i++)
          dp[0][i] = dp[0][i-1] && (s2[i-1] == s3[i-1]);

        for(int i = 1; i <= size1; i++)
          for(int j = 1; j <= size2; j++)
            dp[i][j] = (dp[i-1][j] && (s1[i-1] == s3[i+j-1])) || (dp[i][j-1] && (s2[j-1] == s3[j+i-1])); //两个状态转移方程

        return dp[size1][size2];
    }
};

8 切分回文子串

本题记录了回文串的3中计算方案(非全部)

  • 利用 string s1 = s2
  • 从两边到中间判断扫描
  • 动规判断s[i到j]的子串是否为回文,避免重复计算

8.1 最长回文子串

【链接】https://www.nowcoder.com/questionTerminal/c15cd9e18e4845758d4c1086963731e2

【题目描述】
找出给出的字符串S中最长的回文子串。假设S的最大长度为1000,并且只存在唯一解

【思考路径】
方案一:两重循环暴力判断
方案二:借鉴8.3,避免重复计算。用一个二维数组,记录dp[i][j]是否为回文串

class Solution {
public:
	bool isPalindrome(string s){
		int low = 0, high = s.size()-1;
		bool flag  = true;
		while(low < high){
			if(s[low] != s[high]){
				flag = false;
				break;
			}
			low++;
			high--;
		}
		return flag;
	}
    
    string longestPalindrome(string s) {
    	if(s.size() <= 1)
    		return s;

    	int max_len = 0;
    	int max_len_start_idx = 0;
    	for(int i = 0; i < s.size(); i++){
    		for(int j = i; j < s.size(); j++){
    			if(isPalindrome(s.substr(i,j-i+1))){
    				if(j-i+1 > max_len){
    					max_len = j-i+1;
    					max_len_start_idx = i;
    				}
    			}
    		}
    	}
    	return s.substr(max_len_start_idx, max_len);
    }
};

8.2 输出全部切分方案(DFS)

【链接】
https://www.nowcoder.com/questionTerminal/f983806a2ecb4106a17a365a642a9632

【题目描述】
给定一个字符串s,分割s使得s的每一个子串都是回文串
返回所有的回文分割结果。(注意:返回结果的顺序需要和输入字符串中的字母顺序一致。)
例如:给定字符串s=“aab”,
返回
[↵ [“aa”,“b”],↵ [“a”,“a”,“b”]↵ ]

【题解】

  • 如果要求输出所有可能的解,往往都是要用深度优先搜索。如果是要求找出最优的解,或者解的数量,往往可以使用动态规划。
  • 判断string str是否问回文串的两种写法
  • 因为是全切分,所以sub_str的首位下标必须是0,直接遍历切分长度即可
class Solution {
public:
    //神来之笔,当考察重点不是回文字符串判断时,可以利用这个函数
    bool isPalindrome(string s){
        return s == string(s.rbegin(),s.rend()); 
    }
    
    void dfs(vector<vector<string>>& res, vector<string>& cur, string s){
        if(s == "")
            res.push_back(cur);
        
        //巧妙的解决了substr 的left、len的获取问题
        for(int i = 1; i <= s.length(); i++){ //迭代的是长度,不是下标
            string sub = s.substr(0, i); //start必须为0,因为全切分为回文子串
            if(isPalindrome(sub)){
                cur.push_back(sub);
                dfs(res, cur, s.substr(i, s.length() - i));
                cur.pop_back();
            }
        }
    }
    
    vector<vector<string> > partition(string s) {
        vector<vector<string>> res;
        vector<string> cur;
        dfs(res, cur, s); //与全排列递归不一样的地方是,这里递归的是长度,不是开始位置
        return res;
    }
};

8.3 最小切分数方案(动态规划)

【链接】
https://www.nowcoder.com/profile/9597001/codeBookDetail?submissionId=19237453

【题目描述】
给出一个字符串s,分割s使得分割出的每一个子串都是回文串
计算将字符串s分割成回文分割结果的最小切割数
例如:给定字符串s=“aab”,
返回1,因为回文分割结果[“aa”,“b”]是切割一次生成的。

【一些理解】

  • 动态规划的题,最主要就是写出状态转移方程。状态转移,其实就是怎么把一个大的状态表示为两个或者多个已知的状态
  • 动态规划本质上是暴力搜索,只不过咋这个暴力搜索的过程中,减少了不必要的计算,这样就提升了算法解决问题的速度

【解析】

  1. dp[i]表示当前i到len-1这段的最小分割数
  2. dp[i]=min{dp[j+1]+1}(i=<j<len)其中str[i…j]必须是回文、
  3. p[i][j]=true表示str[i…j]是回文
  4. p[i][j]=s.charAt(i)==s.charAt(j) && (j-i<2||p[i+1][j-1])
class Solution {
public:
    int minCut(string s) {
        vector<int> dp(s.length()+1);
        dp[s.length()] = -1;//确保dp[s.length()-1]=0
        
        //构建一个二维数组记录s[i到j]是否为回文串,避免重复计算
        vector<vector<bool>> p(s.length(), vector<bool>(s.length(), false));
        
        for(int i = s.length()-1; i >= 0; i--){
            dp[i] = INT_MAX;
            for(int j = i; j < s.length(); j++){
                //回文判断;&&前为必要条件
                //后者j-i<2较好理解,b或者bb情形
                //i从大到小,j从小到大。当内层p[i+1][j-1] == true,同时s[i] = s[j]时,p[i][j]也为true
                if(s[i] == s[j] && (j - i < 2 || p[i+1][j-1])){
                    p[i][j] = true;
                    dp[i] = min(dp[i], dp[j+1]+1);
                }
            }
        }
        return dp[0];
    }
};

9 最长公共子串(要求位置连续)

【链接】
https://www.nowcoder.com/questionTerminal/5bb66d2ceb3a433aa6e1c3e254554b15

相同题目(题中描述有误解)
https://www.nowcoder.com/questionTerminal/02e7cc263f8a49e8b1e1dc9c116f7602

【题目描述】
有两个字符串(可能包含空格),请找出其中最长的公共连续子串,输出其长度。

示例1
输入
abcde
bcd
输出
3

【思考&注意点】

  • 需要先初始化第一行、第一列的值,否则[相同题目]不能通过全部测试用例
  • 如果需要输出子串,如何修改代码。在判断是否大于max_len时,增加变量记录起始位置start,然后sub_str(start, max_len)。注意从行取下标,则从行对应的string取子串
  • 如何只能用一维数组记录dp,如何解决 https://www.cnblogs.com/dartagnan/archive/2011/10/06/2199764.html

【详解】

LCS问题就是求两个字符串最长公共子串的问题。解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.

下面是字符串21232523311324和字符串312123223445的匹配矩阵,前者为X方向的,后者为Y方向的。不难找到,红色部分是最长的匹配子串。通过查找位置我们得到最长的匹配子串为:21232
在这里插入图片描述
但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。下面是新的矩阵生成方式:
在这里插入图片描述
当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。

算法的基本思想:

  • 当字符匹配的时候,不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。

  • 我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断

  • 当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。

【DP】

  • State:
    • dp[i][j]: 表示以s1[i]为结尾的子串和以s2[j]为结尾的子串的最长公共连续子串的长度
  • Initial State:
    • dp[i][0] = (s1[i] == s2[0]) ? 1 : 0; when j == 0
    • dp[0][j] = (s1[0] == s2[j]) ? 1 : 0; when i == 0
  • State Transition:
    • dp[i][j] = (s1[i] == s2[j]) ? dp[i - 1][j - 1] + 1 : 0; (i > 0 && j > 0)
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main(){
    string a, b;
	while(cin >> a >> b){
		int a_len = a.length();
		int b_len = b.length();

		vector<vector<int>> dp(a_len, vector<int> (b_len, 0)); //初始值为0
		int max_len = 0; //记录最长公共连续子串长度

		int i, j;
		//第一行、第一列初始化
        for (i = 0; i < a_len; i++){
            if (a[i] == b[0])
                dp[i][0] = 1;
        }
        for (j = 1; j < b_len; j++){
            if (a[0] == b[j])
                dp[0][j] = 1;
        }
        
		for(i = 1; i < a_len; i++){
			for(j = 1; j < b_len; j++){
				if(a[i] == b[j]){
					dp[i][j] = dp[i-1][j-1] + 1; //左上角
					if(dp[i][j] > max_len)
						max_len = dp[i][j];
				}		
			}
		}

		cout << max_len << endl;
	}
	return 0;
}

【DP优化】二维数据改成一维数组

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main(){
    string a, b;
	while(cin >> a >> b){
		int a_len = a.length();
		int b_len = b.length();

		vector<int> dp(b_len, 0); //初始值为0
		int max_len = 0; //记录最长公共连续子串长度
        
        if(a_len > 0 && b_len > 0 && a[0] == b[0])
            dp[0] = 1;
        
		for(int i = 1; i < a_len; i++){
			for(int j = b_len-1; j >= 0; j--){ //从后向前,一维数组当二维数组用
				if(a[i] == b[j]){
					dp[j] = dp[j-1] + 1;
					if(dp[j] > max_len)
						max_len = dp[j];
                }
                else{
                    dp[j] = 0;
                }
			}
		}

		cout << max_len << endl;
	}
	return 0;
}

10 最长公共子序列(不要求位置连续)

【链接】
https://www.nowcoder.com/questionTerminal/9ae56e5bdf4f480387df781671db5172

【题目描述】
我们有两个字符串m和n,如果它们的子串a和b内容相同,则称a和b是m和n的公共子序列。子串中的字符不一定在原字符串中连续。
例如字符串“abcfbc”和“abfcab”,其中“abc”同时出现在两个字符串中,因此“abc”是它们的公共子序列。此外,“ab”、“af”等都是它们的字串。
现在给你两个任意字符串(不包含空格),请帮忙计算它们的最长公共子序列的长度。

【解析】

建立一个dp矩阵,是两个字符串一个纵对应,一个横向对应。
dp[i][j]表示str1[0…i]和str2[0…j]的最长公共子序列的长度
求解 dp[i][j]为:
若str1[i] == str2[j],则 dp[i][j] = max( dp[i-1][j], dp[i][j-1], dp[i-1][j-1]+1 )
若str1[i] != str2[j],则 dp[i][j] = max( dp[i-1][j], dp[i][j-1] )

最后返回dp[M-1][N-1],M、N为两个字符串的长度
在这里插入图片描述

【思考】

  • 如何返回最大子串,如果有多条相同长度最大子串
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
 
using namespace std;
 
int main()
{
    string A, B;
    while(cin >> A >> B) {
        int a_len = A.length();
        int b_len = B.length();

        vector<vector<int> > dp(a_len, vector<int>(b_len, 0));
        int i, j;

        // 初始化边界
        dp[0][0] = (A[0] == B[0]) ? 1 : 0; //因为后序有i-1,j-1的下标索引操作
        for(i = 1; i < a_len; i++) {
        	if(A[i] == B[0])
        		dp[i][0] = 1;
            dp[i][0] = max(dp[i-1][0], dp[i][0]);
        }
        for(j = 1; j < b_len; j++) {
        	if(A[0] == B[j])
        		dp[0][j] = 1;
            dp[0][j] = max(dp[0][j-1], dp[0][j]);
        }

        // 计算
        for(i = 1; i < a_len; i++) {
            for(j = 1; j < b_len; j++) {
                if(A[i] == B[j])
                    dp[i][j] = dp[i-1][j-1] + 1;
                else
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);

				/*
            	//较为巧妙解决了,当值相等时,如何从dp[i-1][j]、dp[i][j-1]、dp[i-1][j-1]+1中取最大值
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
                if(A[i] == B[j])
                	dp[i][j] = max(dp[i][j], dp[i-1][j-1]+1);
                */
            }
        }

        cout << dp[a_len-1][b_len-1] << endl;
    }
 
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值