LeetCode刷题笔记——最长公共(子串、子序列、前缀)问题

LeetCode刷题笔记——最长公共(子串、子序列、前缀)问题

最长公共前缀

题目:14.最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 1:
输入: ["flower","flow","flight"]
输出: "fl"

示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。

说明:
所有输入只包含小写字母 a-z 。

说明:

所有输入只包含小写字母 a-z 。

思路
对于公共前缀问题,可以采用字典树来解决。题目说明输入中只包含a-z字母,所有我们构造的字典树的next指针只有26中状态。按照题意构造的字典树如下:

struct DictTree{
	//用来存储该位置为该字符的字符串数量
    int count;
    //下一个位置的指针,26中情况
    DictTree* next[26];
    DictTree(){
    	//next全部初始化为NULL
        memset(next,0,sizeof(next));
        //创建结点的时候,就有一个字符串该位置是它。
        count=1;
    }
};

按照题意构造出来字典树后,再使用广度优先搜索遍历字典树。对于每个存在的结点,且count等于字符串数量,即为公共前缀元素。

  • 完整代码
struct DictTree{
    int count;
    DictTree* next[26];
    DictTree(){
        memset(next,0,sizeof(next));
        count=1;
    }
};
class Solution {
public:
    void insert(DictTree* root,const string &str){
        for(const char &ch : str){
            int idx=ch-'a';
            if(root->next[idx]==NULL){
                root->next[idx]=new DictTree(); 
            }
            else{
                root->next[idx]->count++;
            }
            root=root->next[idx];
        }
    }
    void bfs(DictTree* root,string & res,int len){
        for(int i=0;i<26;i++){
            if(root->next[i]&&root->next[i]->count==len){
                res.push_back(char(i+'a'));
                bfs(root->next[i],res,len);
            }     
        }
    }
    string longestCommonPrefix(vector<string>& strs) {
        DictTree *root=new DictTree();
        int len=strs.size();
        for(const string &str : strs)
            insert(root,str);
        string result="";
        bfs(root,result,len);
        return result;
    }
};

最长公共子串

题目描述
给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。
输入描述:
输入包括两行,第一行代表字符串srr1,第二行代表字符串str2。\left( 1\leq length(str1),length(str2) \leq 5000 \right)(1≤length(str1),length(str2)≤5000)
输出描述:
输出包括一行,代表最长公共子串。

示例1
输入
复制
1AB2345CD
12345EF
输出
复制
2345
备注:
时间复杂度O(n^{2})O(n^2)),额外空间复杂度O(1)O(1)。(n可以为其中任意一个字符串长度)

思路
求解两个字符串的公共子串问题很明显可以使用动态规划来解决。与最长公共子序列类似,我们设置dp数组,算法框架类似,只是在字符不相等的时候处理方法有所区别:
最长公共子序列不要求连续,所以不相等的时候可以接着找。最长公共子串要求连续,所以不相等是,dp清零。
我们的dp数组中不存最长子串,而是存放子串长度。当长度大于maxlen时,记录子串的结尾index和子串长度。最后,在输入字符串中截取最长子串。

  • 代码
#include<bits/stdc++.h>
using namespace std;
string solve(string str1,string str2){
    int len1=str1.size();
    int len2=str2.size();
    int maxlen=0;
    int last=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(str1[i-1]==str2[j-1]){
                dp[i][j]=dp[i-1][j-1]+1;
                if(dp[i][j]>maxlen){
                    maxlen=dp[i][j];
                    last=i-1;
                }
            }else{
                dp[i][j]=0;
            }
        }
    }
    if(maxlen>0){
        return str1.substr(last-maxlen+1,maxlen);
    }else{
        return "-1";
    }
}
int main(){
    string str1,str2;
    while(cin>>str1>>str2){
        cout<<solve(str1,str2)<<endl;
    }
}

最长公共子序列

题目:1143. 最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

示例 1:
输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace",它的长度为 3。

示例 2:
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc",它的长度为 3。

示例 3:
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0

代码:

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int len1=text1.size();
        int len2=text2.size();
        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(text1[i-1]==text2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else{
                    dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
                }
            }
        return dp[len1][len2];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值