热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

最长公共子序列(lcs)O(nlogn)算法

本文转自:http:karsbin.blog.51cto.com1156716966387最长公共子序列问题:给定2个字符串,求其最长公共子串。如abcde和db

本文转自:http://karsbin.blog.51cto.com/1156716/966387

最长公共子序列问题:

给定2个字符串,求其最长公共子串。

如abcde和dbada的最长公共字串为bd。

动态规划:dp[i][j]表示A串前i个和B串前j个的最长公共子串的长度。

若A[i] == B[j] , dp[i][j] = dp[i-1][j-1] + 1;
否则 dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
时间复杂度O(N*M)。
dp[i][j]仅在A[i]==B[j]处才增加 ,对于不相等的地方对最终值是没有影响的。
故枚举相等点处可以对其进行优化。
则对于dp[i][j](这里只计算A[i]==B[j]的i和j),取最大的dp[p][q],满足(p
这里也可将其转化为最长递增子序列问题。
举例说明:
A:abdba
B:dbaaba
则1:先顺序扫描A串,取其在B串的所有位置:
    2:a(2,3,5) b(1,4) d(0)。
    3:用每个字母的反序列替换,则最终的最长严格递增子序列的长度即为解。
替换结果:532 41 0 41 532
最大长度为3.
简单说明:上面的序列和最长公共子串是等价的。
对于一个满足最长严格递增子序列的序列,该序列必对应一个匹配的子串。
反序是为了在递增子串中,每个字母对应的序列最多只有一个被选出。
反证法可知不存在更大的公共子串,因为如果存在,则求得的最长递增子序列不是最长的,矛盾。
最长递增子序列可在O(NLogN)的时间内算出。
dp[i] = max(dp[j]+1) ( 满足 a[i] > a[j] && i > j )
显然对于同样的如dp[k] = 3,假定k有多个,记为看k1,k2,.....,km 设k1
在计算dp[i]的时候,k2,k3,....,km显然对结果没有帮助,取当前最小的k,
满足ans[k] = p (最小的p使得dp[p]=k) ,每次二分,更新ans[dp[i]] = min(ans[dp[i]],i).
 
ps:LCS在最终的时间复杂度上不是严格的O(nlogn),不知均摊上是不是。
举个退化的例子:

A:aaa

B:aaaa
则序列321032103210

长度变成了n*m ,最终时间复杂度O(n*m*(lognm)) > O(n*m)。

这种情况不知有没有很好的解决办法。

附个参考代码:
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
#include   
 
using namespace std;  
 
const int maxn = 1501 ;  
vector location[26] ;  
int c[maxn*maxn] , d[maxn*maxn] ;  
 
inline int get_max(int a,int b) {   return a > b ? a : b ;  }  
 
//nlogn 求lcs  
int lcs(char a[],char b[])  
{  
    int i , j , k , w , ans , l , r , mid ;  
    for( i = 0 ; i <26 ; i++) location[i].clear() ;  
    for( i = strlen(b)-1 ; i >= 0 ; i--) location[b[i]-'a'].push_back(i) ;  
    for( i = k = 0 ; a[i] ; i++)  
    {  
        for( j = 0 ; j > 1 ;  
            if( d[mid] >= c[i] ) r = mid - 1 ;  
            else l = mid + 1 ;  
        }  
        if( r == ans ) ans++,d[r+1] = c[i] ;  
        else if( d[r+1] > c[i] ) d[r+1] = c[i] ;  
    }  
    return ans ;  
}  
 
int main()  
{  
    char a[maxn] , b[maxn] ;  
    while (~scanf("%s%s",a,b))  
    {  
        printf("%d/n",lcs(a,b));  
    }  
} 
 




推荐阅读
author-avatar
稀释的梦_251
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有