LCS LIS LCIS 字符串编辑距离 专题

来源:互联网 发布:皮卡刻字机端口设置 编辑:程序博客网 时间:2024/06/11 19:41

题目地址 acmore

clone http 点击打开链接 参考: 最大子序列、最长递增子序列、最长公共子串、最长公共子序列、字符串编辑距离

昨天把这个专题刷完了,果真对这些个玩意理解更深了一层。DP过程也清如明镜。

先写一下LIS的nlogn算法:

开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。

这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的''潜力''增大了。

举例:原序列为1,5,8,3,6,7

栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。


if (temp > stack[top])        {            stack[++top] = temp;        }        else        {            int low = 1, high = top;            int mid;            /* 二分检索栈中比temp大的第一个数 */            while(low <= high)            {                mid = (low + high) / 2;                if (temp > stack[mid])                {                    low = mid + 1;                }                else                {                    high = mid - 1;                }            }            /* 用temp替换 */            stack[low] = temp;        }

附POJ 2533:

/*这道题坑了好久,之前用n^2写的。这次用Nlogn写wa4 5 次理解好二分搜索中找到第一个比as[i]大数。二分搜索中要判断到l<=r的时候。等于的时候r变到时候l==r的时候l+1就是了。若等于变l则找不到*/#include <stdio.h>#include <string.h>#include <math.h>#include <bitset>#include <iostream>#include <algorithm>#define inf 0x3fffffffconst int maxn=100000+100;using namespace std;int N,M;int as[maxn];//排序后数组int dp[1024];int main(){    int T;    int n,m,cas=1;while(~scanf("%d",&N)){        int len=0;for(int i=0;i<N;i++)        {            scanf("%d",as+i);            if(i==0) {dp[++len]=as[i];continue;}            if(as[i]>dp[len])                dp[++len]=as[i];            else            {                int l=1,r=len,mid;                while(l<=r)                {                    mid=(l+r)>>1;                    if(dp[mid]<as[i]) l=mid+1;                    else r=mid-1;                }                    dp[l]=as[i];            }        }        printf("%d\n",len);}    return 0;}


第一个,LCS 不多说。

第二个,原来求最少添加回文串只需倒置再求LCS

第三个,依旧LCS,只不过将a[i]==b[j] 转化为 可能再hash一次

第四个,可以直接贪心,题目特点是要都打到(这个题目中顺序不可变)则LIS中的都可以将它前面非LIS数打下来。

/*每一个导弹最终的结果都是要被打的,如果它后面有一个比它高的导弹,  那打它的这个装置无论如何也不能那个导弹了,经过这么一分析,*/这个问题便抽象成在已知序列里找最长上升序列的问题。剩下就很简单了

第五个,刚开始就看出来和第四个很像,但是这个序可以变,于是按照W降序,H降序排出来,过了样例,过了discuss数据。可WA.......

问题就出现在W相同时处理错误。题意是相同必然不能Nest。如果看明白这点用n^2算法是没问题的,但题目要求的时间复杂度需要nlogn型。

这个就得明白为什么排序时需要W降序,H升序。nlogn算法中,在dp数组中存放的是以dp[i]结尾的Len=i(若LIS,每次都将该长度最小值更新进来,使得可以更长)

按照这个思路来,如果H是递减的,并不是最合适的会放入 eg

2015.3.15补充:其实对于W采用了贪心的策略,在满足条件情况下用最合适的,给后面的同H的套娃更大的选择.

W 6  6  6  6  6  5  5  5  5

H  6  5  4  3  2  1  2  4  5

目前是将W由逆序排序,H不排,(其实H按逆序排序分析结果是一样的),那么由于我们此时只考虑H而不考虑W,所以按照求最长升序子序列的方法(这里需要先知道一下求最长升序子序列nlogn的解法),W为6的H为6的那个娃娃会把W为5H为1的包含进去,W为6H为5的娃娃会把W为5H为2的包含进去,而之后W为6的却都不可以把之后w为5的放在它内部了,所以此时结果就是7,而实际上6->5,5->4,4->2,3->1我们就只需要5个娃娃就行了。


但是千万别忘了dp数组中是递增的!!!!!这样放入的是非最优。

思考思维模型,在模型中的情况。增减的影响。相同值时候的情况。
离散化,关心的是序而不是数字本身是什么

第六个,LCIS不多说。百度文库

第七个,枚举对称点(LCIS的变形)。在枚举时候可以优化一层循环。在计算mid的时候,i从1~mid循环了一次,而计算mid+1时i从1~mid又计算了一次,而实际上这两次计算的结果f[N~(mid+1)]都是一样的(因为相当于a序列是a[1~mid],b序列是a[N~mid+1],两次计算他们的最长公共升序子序列的长度肯定是一样的),那么为了免去这个重复的计算,在mid取到mid+1的时候,i便直接从mid+1开始,就可以直接使用前面已经计算出来的mid的所有的值,而省去x从1~mid的重复计算

from F


许多程序会大量使用字符串。对于不同的字符串,我们希望能够有办法判断其相似程序。我们定义一套操作方法来把两个不相同的字符串变得相同,具体的操作方法为:

  1.修改一个字符(如把“a”替换为“b”);

  2.增加一个字符(如把“abdd”变为“aebdd”);

  3.删除一个字符(如把“travelling”变为“traveling”);

    比如,对于“abcdefg”和“abcdef”两个字符串来说,我们认为可以通过增加/减少一个“g”的方式来达到目的。上面的两种方案,都仅需要一 次 。把这个操作所需要的次数定义为两个字符串的距离,而相似度等于“距离+1”的倒数。也就是说,“abcdefg”和“abcdef”的距离为1,相似度 为1/2=0.5。在此我们只需考虑字符串编辑距离即可。


  原文的分析与解法  

  不难看出,两个字符串的距离肯定不超过它们的长度之和(我们可以通过删除操作把两个串都转化为空串)。虽然这个结论对结果没有帮助,但至少可以知道,任意两个字符串的距离都是有限的。

  我们还是就住集中考虑如何才能把这个问题转化成规模较小的同样的子问题。如果有两个串A=xabcdae和B=xfdfa,它们的第一个字符是 相同的,只要计算A[2,...,7]=abcdae和B[2,...,5]=fdfa的距离就可以了。但是如果两个串的第一个字符不相同,那么可以进行 如下的操作(lenA和lenB分别是A串和B串的长度)。

 1.删除A串的第一个字符,然后计算A[2,...,lenA]和B[1,...,lenB]的距离。

  2.删除B串的第一个字符,然后计算A[1,...,lenA]和B[2,...,lenB]的距离。

  3.修改A串的第一个字符为B串的第一个字符,然后计算A[2,...,lenA]和B[2,...,lenB]的距离。

  4.修改B串的第一个字符为A串的第一个字符,然后计算A[2,...,lenA]和B[2,...,lenB]的距离。

  5.增加B串的第一个字符到A串的第一个字符之前,然后计算A[1,...,lenA]和B[2,...,lenB]的距离。

  6.增加A串的第一个字符到B串的第一个字符之前,然后计算A[2,...,lenA]和B[1,...,lenB]的距离。

  在这个题目中,我们并不在乎两个字符串变得相等之后的字符串是怎样的。所以,可以将上面的6个操作合并为:

  1.一步操作之后,再将A[2,...,lenA]和B[1,...,lenB]变成相字符串。

  2.一步操作之后,再将A[2,...,lenA]和B[2,...,lenB]变成相字符串。

  3.一步操作之后,再将A[1,...,lenA]和B[2,...,lenB]变成相字符串。

  如果熟悉动态规划的人很容易就看到这里使用动态规划最好,如果使用递归的话会重复计算很多子问题的。

这里是自底向上的动态规划,比自顶向下的动态规划快一个常数因子的速度,具体可以看看另一个博文[算法]添加最少字符数构成使字符串构成回文其中有自顶向下的,也有自底向上的两种做法。在这里就直接用自底向上的动态规划。有一个小技巧就是使用了滚动数组,空间复杂度降低了一个数量级。

#include <iostream>using namespace std;const int N = 1001;int dp[2][N];int foo(char* src, int srcLen, char* des, int desLen){    int i, j;    for(j = 0; j <= desLen; ++j)    {        dp[0][j] = j;     }    int a, b;    for(i = 1; i <= srcLen; ++i)    {        dp[i&1][0] = i;        for(j = 1; j <= desLen; ++j)        {            if(src[i] == des[j])            {                dp[i&1][j] = dp[(i-1)&1][j-1];            }            else            {                a = dp[i&1][j-1] + 1;                b = dp[(i-1)&1][j] + 1;                dp[i&1][j] = a < b ? a : b;            }        }    }    return dp[srcLen&1][desLen];}int main(int argc, char** argv) {     char src[N]="abcd",des[N]="b55cd";    cout<<foo(src,4,des,5);    return 0;}


0 0
原创粉丝点击