注:サブシーケンスは連続している必要はありません。サブストリングは連続している必要があります。
1.最長共通サブ
シーケンス: 2つのシーケンスを指定して、2つのシーケンスの最長共通サブシーケンスを見つけます。例:
1.1。最適サブ構造:
1.2。疑似コード:
マトリックスcおよびbの可視化:
1.3。C ++コード:
#include <iostream>
#include <vector>
#include <string>
#include <stack>
using namespace std;
string getLCS(string s1, string s2)
{
//此部分获得最长值
int m=s1.length()+1;
int n = s2.length()+1;
vector< vector<int> > c(m, vector<int>(n)); //计算长度
vector< vector<int> > b(m, vector<int>(n)); // 字串遍历
for(int i = 0;i<m;i++) {
c[i][0] = 0; } // 初始条件(边界条件)
for(int i = 0;i<n;i++) {
c[0][i] = 0; } // 同上
for(int i = 0;i<m-1;i++){
for(int j = 0;j<n-1;j++){
if(s1[i] == s2[j]) {
c[i+1][j+1] = c[i][j] + 1;
b[i+1][j+1] = 1; // 1 表示相同
}
else if(c[i][j+1]>c[i+1][j]){
c[i+1][j+1] = c[i][j+1];
b[i+1][j+1] = 2; // 2 表示不同
}
else{
c[i+1][j+1] = c[i+1][j];
b[i+1][j+1] = 3; // 3表示不同
}
}
}
//此部分获得公共子序列
stack<char> LCSans;
for(i = m-1,j = n-1; i>=0 && j>=0; ;){
if(b[i][j] == 1){
LCSans.push_back(s1[i]);
i--;
j--;
}
else if(b[i][j] == 2)
i--;
else
j--;
}
// 此部分输出子序列
while(!LCSans.empty()) {
cout<<LCSans.top();
LCSans.pop();
}
}
参照:最長共通サブシーケンス(LCS)の動的プログラミングソリューション
2.最も長い共通部分文字列:最長の部分列判断文を
置き換えますelse if
string getLCS(string str1, string str2) {
int m=s1.length()+1;
int n = s2.length()+1;
int maxlen = 0;
int end;
vector< vector<int> > c(m, vector<int>(n)); //计算长度
for(int i = 0;i<m;i++) {
c[i][0] = 0; } // 初始条件(边界条件)
for(int i = 0;i<n;i++) {
c[0][i] = 0; } // 同上
for(int i = 0;i<m-1;i++){
for(int j = 0;j<n-1;j++){
if(s1[i] == s2[j]) {
c[i+1][j+1] = c[i][j] + 1;
}
else {
c[i+1][j+1] = 0;
}
if(c[i+1][j+1]>maxlen){
maxlen = c[i+1][j+1];
end = i;
}
}
}
return str1.substr(end - maxlen + 1, maxlen);
}
3.最長の回文サブシーケンス:
文字列の文字は対称的であり、回文と呼ばれます。たとえば、文字列wabcdcbwqのwbcdcbwは回文サブシーケンス、bcdcbは回文サブシーケンスです。
解決策:
文字列sを反転してs 'に変更し、一般的なサブシーケンス法を使用して解決します。
string longestPalindrome(string s) {
if(s.length()==1) return s;//大小为1的字符串必为回文串
string rev=s;//rev存放s反转结果
string res;//存放结果
std::reverse(rev.begin(),rev.end());
if(rev==s) return s;
//从此开始调用最长公共子序列算法。
4.最も長い回文部分文字列:
4.1。動的プログラミングバージョン:
Sは文字列を表し、iとjは開始位置と終了位置を表します。
初期条件(境界条件):長さが2未満の場合、dp(i + 1、j -1)の範囲が間違っているため、長さは1、2が境界条件です
a)dp [i] [i] = true(コードで使用) 1の意味)
b)S [i] = S [i + 1]の場合、dp [i] [i + 1] = true
状態遷移方程式:
a)If(dp(i + 1、j -1)== true && S [i] == S [j])then dp(i、j)= true
b)if(dp(i + 1、j -1)== false || S [i]!= S [j ])次にdp(i、j)= false
class Solution {
public:
string longestPalindrome(string s) {
int len=s.size();
if(len==0||len==1)
return s;
int start=0;//回文串起始位置
int max=1;//回文串最大长度
vector<vector<int>> dp(len,vector<int>(len));//定义二维动态数组
for(int i=0;i<len;i++)//初始化状态
{
dp[i][i]=1; //单个元素是回文串
if(i<len-1&&s[i]==s[i+1]) //两个元素相同也是回文串
{
dp[i][i+1]=1;
max=2;
start=i;
}
}
for(int l=3;l<=len;l++)//l表示检索的子串长度,等于3表示先检索长度为3的子串
{
for(int i=0;i+l-1<len;i++)
{
int j=l+i-1;//终止字符位置
if(s[i]==s[j]&&dp[i+1][j-1]==1)//状态转移
{
dp[i][j]=1;
start=i;
max=l;
}
}
}
return s.substr(start,max);//获取最长回文子串
}
};
参考資料:最長回文部分文字列のC ++ソリューション3:動的プログラミング
4.2。中央拡張法:
中央拡散法でテキスト文字列を取得する方法?
それぞれの位置から始めて、両側に広げます。回文ではないときに終了します。たとえばstr=acdbbdaa
、最初のb(位置3)から始まる最長の回文を見つける必要があります。見つけ方?
まず、不平等に遭遇するまで、左側の現在の位置と同じ文字を探します。
次に、右に移動して、現在の位置と同じ文字を探します。
最後に、左と右が等しくなくなるまで両方向に広がります。次の図に示すように、
ウィンドウサイズ(len)が両側に広がる各位置に表示されます。If len>maxLen
(最長の回文の長さを示すために使用されます)。更新されたmaxLen
値。
最後に返すのは、長さではなく特定の部分文字列であるため、この時点で、開始位置(maxStart)をmaxLen、つまりmaxStart = lenに記録する必要もあります。
string LPS(string s)
{
int maxstart = 0;
int maxlen = 1;
int start = 0;
int len = 1;
if(s.size()<=1) return s;
for(int i = 0;i<s.size();i++){
int l = i--;
int r = i++;
while(l>=0 && s[l] == s[i]) {
start = l; len++; l--}
while(r<s.size() && s[r] == s[i]) {
len++; r++}
while(l>=0 && r<s.size() && s[l] == s[r]) {
start= l;
l--;
r++;
len = len+2;
}
if(len > maxlen) {
maxstart = start; maxstart = start;}
len = 1;
}
return s.substring(maxstart,maxlen);
}
参考資料:中央拡散法
総括する:
1.動的プログラミングの鍵:最適な部分構造を見つける、つまり初期状態と状態遷移方程式を見つける。