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];
}
};