String 最长公共回文子序列
有没有有效的算法计算两个给定字符串的最长公共回文子序列的长度 例如: 字符串1<代码>afbcdfca 字符串2<代码>bcadfcgyfka LCPS为5,LCPS字符串为String 最长公共回文子序列,string,algorithm,lcs,String,Algorithm,Lcs,有没有有效的算法计算两个给定字符串的最长公共回文子序列的长度 例如: 字符串1afbcdfca 字符串2bcadfcgyfka LCPS为5,LCPS字符串为afcfa是 只需对两个以上的序列使用LCS算法 如果我没弄错的话 LCS( afbcdfca, acfdcbfa, bcadfcgyfka, akfygcfdacb) = afcfa 由你自己来决定字符串2和4 更新:不,这里有一个反例:LCS(aba,aba,bab,bab)=ab,ba。LCS不能确保子序列是回文,您可能需要添加此
afcfa
是
只需对两个以上的序列使用LCS算法
如果我没弄错的话
LCS( afbcdfca, acfdcbfa, bcadfcgyfka, akfygcfdacb) = afcfa
由你自己来决定字符串2和4
更新:不,这里有一个反例:LCS(aba,aba,bab,bab)=ab,ba。LCS不能确保子序列是回文,您可能需要添加此约束。无论如何,LCS的递推方程是一个很好的起点
如果以生成器样式实现LCS,以便生成长度为n、n-1、n-2等的所有LCS,那么您应该能够相当高效地计算LCS gen(string1,reverse-string1)、LCS gen(string2,reverse-string2)中最长的公共成员。但我还没有检查,如果有高效的LCS-gen。与此问题相同: 在下面的代码中,我给您一个cd(s)方法(它返回一个char dict,告诉您字符串中下一个或上一个字符的位置等于该字符)。使用它来实现一个动态编程解决方案,我还为您提供了示例代码 在动态规划中,只有len(s1)*len(s1)/2个状态,因此顺序(N^2)是可能的 想法是要么从s1中获取一个字符,要么不获取它。 如果从s1上取字符,则必须从前面和后面(s1和s2的前面和后面)取字符。如果你不接受,你就继续前进。(这意味着s2的状态与s1的状态保持同步,因为您总是贪婪地从两者的外部获取—因此您只需要担心s1可以有多少个状态) 这段代码可以帮助您完成大部分任务。cd1(char dict 1)帮助您在s1中找到与当前字符相等的下一个字符(向前和向后) 在递归solve()方法中,您需要决定什么是start1,end1。。等应该是。每次接受一个字符时,在总数中加2(除非start1==end1-然后加1)
s1=“afbcdfca”
s2=“bcadfcgyfka”
def光盘:
“”“返回字典d,其中d[i]=j,其中j是字符i的下一个匹配项”“”
char_dict={}
最后位置={}
对于i,枚举中的字符:
如果char\u dict中的char:
_,向前,向后=char\u dict[char]
pos=最后一个位置[char]
向前[pos]=i
向后[i]=pos
最后位置[char]=i
其他:
首先,向前,向后=i,{},{}
char_dict[char]=(第一,向前,向后)
最后位置[char]=i
返回字符
打印cd(s1)
“{'a':({0:7},{7:0}),'c':({3:6},{6:3}),'b':({},{}),'d':({},{}),'f':({1:5},{5:1})”
cd1,cd2=cd(s1),cd(s2)
缓存={}
def解算(开始1、结束1、开始2、结束2):
状态=(开始1,结束1)
answer=cache.get(状态,无)
如果回答:
回覆
如果start1
这是我的一行一行的简单演练,因为这是很常见的,大多数时候人们70%地解释动态编程部分,而停留在血淋淋的细节上
1) 最佳子结构:
设X[0..n-1]
为长度n的输入序列,L(0,n-1)
为X[0..n-1]的最长回文子序列的长度。
如果X的最后和第一个字符相同,则L(0,n-1)=L(1,n-2)+2
。等等,为什么,如果第二个和第二个到最后一个字符不是
同样,最后一个和第一个必应相同,不是没有用吗。不,这个“子序列”不必是连续的
/* Driver program to test above functions */
int main()
{
char seq[] = "panamamanap";
int n = strlen(seq);
printf ("The lnegth of the LPS is %d", lps(seq, 0, n-1));
getchar();
return 0;
}
int lps(char *seq, int i, int j)
{
// Base Case 1: If there is only 1 character
if (i == j)
return 1;
// Base Case 2: If there are only 2 characters and both are same
if (seq[i] == seq[j] && i + 1 == j)
return 2;
// If the first and last characters match
if (seq[i] == seq[j])
return lps (seq, i+1, j-1) + 2;
// If the first and last characters do not match
else return max( lps(seq, i, j-1), lps(seq, i+1, j) );
}
考虑到上面的实现,下面是一个长度为6且具有所有不同字符的序列的部分递归树
L(0, 5)
/ \
/ \
L(1,5) L(0,4)
/ \ / \
/ \ / \
L(2,5) L(1,4) L(1,4) L(0,3)
在上面的部分递归树中,L(1,4)
被求解了两次。如果我们画出完整的递归树,那么我们可以看到
许多子问题一次又一次地被解决。与其他典型的动态规划(DP)问题一样,相同子问题的重新计算
可以通过自下而上构造临时数组L[][]
来避免
//返回seq中最长回文子序列的长度
int lps(char *str)
{
int n = strlen(str);
int i, j, cl;
int L[n][n]; // Create a table to store results of subproblems
// Strings of length 1 are palindrome of length 1
for (i = 0; i < n; i++)
L[i][i] = 1;
for (cl=2; cl<=n; cl++) //again this is the length of chain we are considering
{
for (i=0; i<n-cl+1; i++) //start at i
{
j = i+cl-1; //end at j
if (str[i] == str[j] && cl == 2) //if only 2 characters and they are the same then set L[i][j] = 2
L[i][j] = 2;
else if (str[i] == str[j]) //if greater than length 2 and first and last characters match, add 2 to the calculated value of the center stripped of both ends
L[i][j] = L[i+1][j-1] + 2;
else
L[i][j] = max(L[i][j-1], L[i+1][j]); //if not match, then take max of 2 possibilities
}
}
return L[0][n-1];
}
intlps(char*str)
{
int n=strlen(str);
int i,j,cl;
int L[n][n];//创建一个表来存储子问题的结果
//长度为1的字符串是长度为1的回文
对于(i=0;i 对于(cl=2;cl)这与回文有什么关系?啊,我明白了,LCP是“afcfa”,而不是“afca”。动态规划问题。请看w.r.t DPI谷歌搜索了你的请求,找到了一篇关于arxiv的论文。@Kwariz这确实是一篇关于LCP的好文章。但它的复杂性是O(n^4)!我在这里问这个问题是想知道是否有运行时间更短的算法。是的,这是可能的:LCS(LCS(string_1,reverse_string_1),LCS(string_2,reverse_string_2))。但问题是LCS函数必须返回所有可能的LCS,因为两个字符串中可能有多个LCS。因此我必须运行外部LCS()
int lps(char *str)
{
int n = strlen(str);
int i, j, cl;
int L[n][n]; // Create a table to store results of subproblems
// Strings of length 1 are palindrome of length 1
for (i = 0; i < n; i++)
L[i][i] = 1;
for (cl=2; cl<=n; cl++) //again this is the length of chain we are considering
{
for (i=0; i<n-cl+1; i++) //start at i
{
j = i+cl-1; //end at j
if (str[i] == str[j] && cl == 2) //if only 2 characters and they are the same then set L[i][j] = 2
L[i][j] = 2;
else if (str[i] == str[j]) //if greater than length 2 and first and last characters match, add 2 to the calculated value of the center stripped of both ends
L[i][j] = L[i+1][j-1] + 2;
else
L[i][j] = max(L[i][j-1], L[i+1][j]); //if not match, then take max of 2 possibilities
}
}
return L[0][n-1];
}