精华内容
下载资源
问答
  • 当前实现了十二种算法(包括Levenshtein编辑距离和同级,Jaro-Winkler,最长公共子序列,余弦相似性等)。 查看下面的摘要表以获取完整列表... 下载 使用Maven: <groupId>info.debatty <artifactId>java-...
  • 最长公共子序列编辑距离,最长公共串 最长公共子序列编辑距离,最长公共串求文本的相似度与向量夹角求文本的相似度
    前言

    最长公共串是编辑距离与最长公共子序列的基础,而编辑距离与最长公共子序列应用之处非常广泛。

    在单词的和词汇的校验是否正确中,会使用到编辑距离;最长公共子序列算法在文本的diff算法,git和svn中比较代码修改前后的差异,两个文本的相似度计算使用最长公共子序列计算将更加准确,能够保留更多的信息(余弦夹角只能根据词频计算)。在多数的算法解释中,都仅仅是提供公式和代码;他们的代码和公式很容易记住。学习是一个理解的过程,不然只会拿着它解决特定问题,而不能进行扩展。

    最长公共串

    字符串strA = "acasdef"和字符串strB = "aasdefd";容易得出最长公共串的asdef。
    最长公共串是计算两个字符串中连续的最长的相同的 字符串。

    行和列相互比较,如果不相等使用0 进行填充,如果相等 取得 左上方的值加1 填充此位置;
    根据如下图的矩阵排列,第二行与第一列的a从第一列开始,依次与第二列比较,直到最后一列。这种排列过程形如,a依次与字符串acasdef进行比较。同理第一行第二列的a,依次从第一行开始,然后第二行第二列a,就相当于a与字符串aasdefd进行比较。
    在这里插入图片描述
    查看一下左上方是什么意思;比如:下图中数字2的左边就是横轴字符退一格,由原来的acas变成aca数字2的上方就是纵轴字符退一个,由原来的aas变成aa 。那么就容易得出了,左上方就是当前横轴strA纵轴strB字符串的前一个字符。
    在这里插入图片描述
    可能不好理解,我们再来整一个图;看绿色背景的对角线的字符,其实就是横轴的ac纵轴的as;蓝色背景的其实就是 sdefaasd,那么橘黄色背景也很容易得出 defefd 了;两个灰色背景的对角分别是 defdacas比较,defdasde

    那么我们得出结论,字符串strA = "acasdef"和字符串strB = "aasdefd",可以出现任何长度的比较。而且先前的 比较的值 已经保存在对应的位置中,那么下次再进行别的位置比较时,若需要用到之前的值,拿来使用即可;这种方法美其名曰动态规划

    在这里插入图片描述

    编辑距离

    编辑距离,横轴和纵轴的字符串计算得出

    最长公共子序列
    展开全文
  • 给出一段序列,选出其中连续且非空的一段使得这段和最大。 输入格式: 第一行是一个正整数N,表示了序列的长度。 第二行包含N个绝对值不大于1e9的整数Ai​,描述了这段序列。 输出格式: 一个整数,为最大的子段和是...

    最大子段和

    1(最大子段和)
    题目描述 :
    给出一段序列,选出其中连续且非空的一段使得这段和最大。
    输入格式:
    第一行是一个正整数N,表示了序列的长度。
    第二行包含N个绝对值不大于1e9的整数Ai​,描述了这段序列。
    输出格式:
    一个整数,为最大的子段和是多少。

    思路:
    对于全部为负数的序列 ,我们只需输出一个最大值即可。
    对于不全为负数的序列 ,我们利用前缀和sum处理,每次更新一个最大值max,当sun<0时,说明后面的数加上此前缀无法变得更大,和则令sun=0,继续处理。

    代码:

    #include <stdio.h>
    long long n,a[1000005],sum,max,MAX=-1e9;
    int main()
    {
    	scanf("%lld",&n);
    	for(int i=0;i<n;i++)
        {
            scanf("%lld",&a[i]);
            if(a[i]>MAX)
                MAX=a[i];
        }
        if(MAX<=0)	
        {
            printf("%lld",MAX);
            return 0;
        }
        max=a[0];
    	for(int i=0;i<n;i++)
    	{
    		sum+=a[i];
    		if(sum<0)
    			sum=0;
    		if(sum>max)
    			max=sum;
    	}
    	printf("%lld",max);
    	return 0;
    }
    

    2 (最大子矩阵和)
    题目描述 :
    给出一个矩阵,求最大非空子矩阵是多少。
    输入格式:
    第一行是正整数N ,M,表示矩阵的行数 列数。
    接下来N行,每行M个数,表示-1e9<=a[i][j]<=1e9。
    输出格式:
    一个整数,为最大的非空子矩阵和是多少。

    思路:我们可以把二维的矩阵,转变为一维的子段和问题处理,所以我们要储存a[i][j]第j列前i行和。

    代码:

    #include <stdio.h>
    long long n,m,a[444][444],b[444][444],s,ans=-1e9;
    int main()
    {
        scanf("%lld%lld",&n,&m);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%lld",&a[i][j]);
                b[i][j]=b[i-1][j]+a[i][j];
                if(ans<a[i][j])
                    ans=a[i][j];
            }
        }
        if(ans<=0)
        {
            printf("%lld",ans);
            return 0;
        }
        else
        {
            for(int i=1;i<=n;i++)
            {
                for(int j=i;j<=n;j++)
                {
                    s=0;
                    for(int k=1;k<=m;k++)
                    {
                        s+=b[j][k]-b[i][k];
                        if(s<0)
                            s=0;
                        else if(s>ans)
                            ans=s;
                    }
                }
            }
        }
        printf("%lld",ans);
        return 0;
    }
    

    最长上升子序列

    1.( 跳木桩)
    面前有一排 n 个木桩,木桩的高度分别是h1,h2,h3⋯hn​。第一步可以跳到任意一个木桩,接下来的每一步不能往回跳只能往前跳,并且跳下一个木桩的高度 不大于 当前木桩。希望能踩到尽量多的木桩,请你计算,最多能踩到多少个木桩。

    输入格式
    第一行输入一个整数 n代表木桩个数。第二行输入 n个整数 分别代表 n 个木桩的高度。(1≤n≤1000,1≤hi​≤100000)

    输出格式
    输出一个整数,代表最多能踩到的木桩个数,占一行。

    样例输入
    6
    3 6 4 1 4 2
    样例输出
    4

    思路:本题的模版是最长不上升子序列,dp[i]表示以第i号元素为结尾的子序列,最长是多少。我们只需要枚举i号之前的元素,有多少不小于h[i]即可。最后输出最大的一个dp[i].

    代码:

    #include <stdio.h>
    int n,h[100005],dp[100005],ans=1;
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&h[i]);
        }
        for(int i=1;i<=n;i++)
        {
            dp[i]=1;
            for(int j=1;j<i;j++)
            {
                if(h[j]>=h[i])
                {
                    dp[i]=max(dp[j]+1,dp[i]);
                    ans=max(dp[i],ans);
                }
            }
        }
        printf("%d",ans);
        return 0;
    }
    

    2(删除最少元素)
    给定n个数 ,满足a[i]>=a[j]···>=a[k]<=···a[p]<=a[q] (其中 i<j<k<p<q)。求在a序列中最少删除多少元素,得到满足题意的序列?

    思路:两个dp

    代码:

    #include <stdio.h>
    int n,a[1005],dp1[1005],dp2[1005],ans;
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++)
        {
            dp1[i]=1;
            for(int j=1;j<i;j++)
            {
                if(a[j]>=a[i])
                {
                    dp1[i]=max(dp1[i],dp1[j]+1);
                }
            }
        }
        for(int i=n;i>=1;i--)
        {
            dp2[i]=1;
            for(int j=n;j>i;j--)
            {
                if(a[j]>=a[i])
                {
                    dp2[i]=max(dp2[i],dp2[j]+1);
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            ans=max(dp1[i]+dp2[i]-1,ans);
        }
        printf("%d",n-ans);
        return 0;
    }
    

    3(优化版本的最长上升子序列)
    给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少。

    输入格式
    第一行包含整数N。

    第二行包含N个整数,表示完整序列。

    输出格式
    输出一个整数,表示最大长度。

    数据范围
    1≤N≤100000,
    −109≤数列中的数≤109
    输入样例:
    7
    3 1 2 1 8 5 6
    输出样例:
    4

    思路:考虑到上述最长子序列算法时间复杂度为n方,而本题n的范围为1e5,所以必须进行优化。
    首先数组a中存输入的数(原本的数),开辟一个数组f用来存结果,最终数组f的长度就是最终的答案;假如数组f现在存了数,当到了数组a的第i个位置时,首先判断a[i] > f[cnt] ? 若是大于则直接将这个数添加到数组f中,即f[++cnt] = a[i];这个操作时显然的。
    当a[i] <= f[cnt] 的时,我们就用a[i]去替代数组f中的第一个大于等于a[i]的数,因为在整个过程中我们维护的数组f 是一个递增的数组,所以我们可以用二分查找在 logn 的时间复杂的的情况下直接找到对应的位置,然后替换,即f[l] = a[i]。
    我们用a[i]去替代f[i]的含义是:以a[i]为最后一个数的严格单调递增序列,这个序列中数的个数为l个。

    这样当我们遍历完整个数组a后就可以得到最终的结果。

    时间复杂度分析:O(nlogn)

    代码:

    #include <stdio.h>
    long long a[100005],f[100005],n,ans=0;
    void er(long long target) 
    {
        int left = 0;
        int right = ans; 
        while (left < right)
        { 
            int mid = (left + right) / 2;
            if (f[mid] == target) 
            {
                right = mid;
            } 
            else if (f[mid] < target)
            {
                left = mid + 1;
            } 
            else if (f[mid] > target)
            {
                right = mid; 
            }
        }
        f[left]= target;
    }
    int main()
    {
        scanf("%lld",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%lld",&a[i]);
        }
        f[ans++]=a[0];
        for(int i=1;i<n;i++)
        {
            if(a[i]>f[ans-1]) f[ans++]=a[i];
            else if(a[i]<f[ans-1])
            {
                er(a[i]);
            }
        }
        printf("%lld",ans);
        return 0;
    }
    
    
    

    最长公共子序列

    1(最长公共子序列)

    思路:
    设X=x1 x2…xm和Y=y1 y2…yn是两个序列,Z=z1 z2…zk是这两个序列的一个最长公共子序列。

    1. 如果xm=yn,那么zk=xm=yn,且Zk-1是Xm-1,Yn-1的一个最长公共子序列;
    2. 如果xm≠yn,那么zk≠xm,意味着Z是Xm-1,Y的一个最长公共子序列;
    3. 如果xm≠yn,那么zk≠yn,意味着Z是X,Yn-1的一个最长公共子序列。

    我们使用dp[i][j]来表示第一个串的前i位和第二个串的前j位中的最长公共子序列,我们很容易能发现当两个串的任意一个串的当前长度为0时,它的最长公共子序列的长度为0,所以先对dp数组的边界进行初始化。然后我们发现,如果a[i]=b[j],dp[i][j]=dp[i-1][j-1]+1,很显然,当比对的位字符一样时,能得到该状态转移方程。如果a[i]≠b[j],dp[i][j]=max(dp[i-1][j],dp[i][j-1]),该状态转移方程是由上面的2,3条取最大值得到的。

    代码:

    #include <stdio.h>
    #include <string.h>
    int len1,len2,dp[1005][1005]; //dp[i][j]表示a前i位和b前j位的最长公共子序列长度
    char a[1005],b[1005];
    int max(int x,int y)
    {
    	return x>y?x:y;
    }
    int main()
    {
        scanf("%s%s",a+1,b+1);
        len1=strlen(a+1);
        len2=strlen(b+1);
        for(int i=1;i<=len1;i++)
        {
            for(int j=1;j<=len2;j++)
            {
                if(a[i]==b[j])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        printf("%d",dp[len1][len2]);
    	return 0;
    }
    

    2
    问题描述
    一个字符串如果从左往右读和从右往左读都一样,那么这个字符串是一个回文串。例如:”abcba”,”abccba”。
    蒜头君想通过添加字符把一个非回文字符串变成回文串。例如:”trit”,可以添加一个’i’ 变成回文串”tirit”。请你用程序计算出,对于一个给定的字符串,最少需要添加几个字符,才能变成回文串。
    输入格式
    输入一个长度为n(1≤n≤3000) 的字符串。(字符串只包含字母)
    输出格式
    输出最少需要添加的字符个数,占一行。

    思路:
    简单分析一些就可以发现:既然是回文串,那么将原串s1倒置得到一个新的字符串s2,求出s1和s2的最长公共子序列长度,用s1的长度减去所求长度就是最少添加字符数量。

    代码:

    #include <stdio.h>
    #include <string.h>
    int len,dp[3010][3010];
    char a[3010],b[3010];
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%s",a);
        len=strlen(a);
        int j=0;
        for(int i=len-1;i>=0;i--)
        {
            b[j++]=a[i];
        }
        b[len]='\0';
        for(int i=len-1;i>=0;i--){
    		for(int j=len-1;j>=0;j--){
    			if(a[i]==b[j]){
    				dp[i][j]=dp[i+1][j+1]+1;
    			}else{
    				dp[i][j]=max(dp[i+1][j],dp[i][j+1]);
    			}
    		}
    	}
        printf("%d",len-dp[0][0]);
        return 0;
    }
    

    最长公共上升子序列

    1
    熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。

    小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。

    小沐沐说,对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。

    奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。

    不过,只要告诉奶牛它的长度就可以了。

    数列A和B的长度均不超过3000。

    输入格式
    第一行包含一个整数N,表示数列A,B的长度。

    第二行包含N个整数,表示数列A。

    第三行包含N个整数,表示数列B。

    输出格式
    输出一个整数,表示最长公共子序列的长度。

    数据范围
    1≤N≤3000,序列中的数字均不超过231−1
    输入样例:
    4
    2 2 1 3
    2 1 2 3
    输出样例:
    2

    思路:dp[i][j]表示a中前i位和b中第j位的最长公共上升子序列。对于a[i],我们遍历b中每个元素,判断a[i]和b[j]的关系,如果a[i]>b[j],那么为了方便后面找到a[i]=b[k]时,dp[i][k]的最长公共子序列,我们记录最大的max=dp[i-1][j](a[i]>b[j])。如果a[i]=b[j],那么dp[i][j]=max+1.

    代码:

    #include <stdio.h>
    int n,m,a[3005],b[3005],dp[3005][3005],ans=-1e9,MAX;   //dp[i][j]代表a中前i位和b中第j位匹配时最长公共上升子序列
    int max(int x,int y)                                  //MAX代表小于b[j]的b[k]所具有的最大子序列长度
    {
        return x>y?x:y;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
            scanf("%d",&b[i]);
        for(int i=1;i<=n;i++)
        {
            MAX=0;       
            for(int j=1;j<=n;j++)
            {
                dp[i][j]=dp[i-1][j];  //如果第i位与j位不相同 则前i位与第j位最大上升子序列要去前i-1位找
                if(a[i]>b[j])   MAX=max(MAX,dp[i-1][j]);  //优化 减小后面遍历寻找b[k]<b[j]的时间复杂度 后面a[i]=b[j]时直接用此值
                else if(a[i]==b[j]) dp[i][j]=MAX+1;  
            }
        }
         for(int j=1;j<=n;j++)
        {
             ans=max(ans,dp[n][j]);  //n j 代表前n位 第j位
        }
        printf("%d",ans);
        return 0;
    }
    

    2
    蒜头君喜欢把做过的事情记录下来,写在日志里,为了安全起见,它还有一份备份放在另外的地方,不过很不幸的是,最近他的两份日志都受到了破坏,有增加删除修改,但没有改变任何原来的顺序,两份受到的破坏不一定一样,蒜头君记录事情都是按时间顺序的,记录的也都是时间戳,所以正确的记录上时间戳肯定是严格递增的,并且只有两份记录上都出现了的时间戳他才认为有可能自己做了,现在他想知道他最多可能做了多少事情。

    输入格式
    第一行输入两个整数n,m代表两份记录的长度。(1≤n,m≤103)

    接下来一行输入n个整数,a1,a2,a3⋯an,代表第一份记录的n个时间戳。(1≤ai≤103)

    接下来一行输入m个整数,b1,b2,b3⋯bm,代表第二份记录的m个时间戳。(1≤bi≤103)

    输出格式
    输出一个整数,代表蒜头君最多可能做了多少事情。

    输入样例
    3 2
    1 3 2
    1 2
    输出样例
    2

    代码:

    #include <stdio.h>
    int n,m,a[1005],b[1005],dp[1005][1005],max;
    int main()//dp[i][j]代表a数组中前i项和b数组中以j项结尾的最长公共上升子序列
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=m;i++)
            scanf("%d",&b[i]);
        for(int i=1;i<=n;i++)
        {
            max=0;
            for(int j=1;j<=m;j++)
            {
                dp[i][j]=dp[i-1][j];//a[i]!=b[i]时
                if(a[i]>b[j]&&max<dp[i-1][j])	max=dp[i-1][j];//记录小于a[i]的最长的长度 后面要用
                else if(a[i]==b[j])	dp[i][j]=max+1;
            }
        }
        int ans=0;
        for(int i=1;i<=m;i++)
        {
            if(ans<dp[n][i])
            {
                ans=dp[n][i];
            }
        }
        printf("%d\n",ans);
        return 0;
    }
    

    编辑距离

    编辑距离参考思路

    每个人都有点秘密,蒜头君也不例外,他把秘密记在一个小本上,并且留有备份,不过第一个本的内容被人破坏掉了,跟原来不一定相同了,他现在想要照着第二个本把第一个本的内容还原,每一次做一个操作,一个操作可以是在某位置增加一个字符,删掉某个字符,或者把某个位置的字符改成另一个字符,他想知道他最少需要进行多少次操作才能把第一个本的内容还原。

    输入格式
    第一行一个字符串A,表示第一个本被破坏之后的字符串。

    第二行一个字符串B,表示第二个本上面的字符串。

    字符串均仅有小写字母组成且长度均不超过1000。

    输出格式
    输出一个整数,为蒜头君最少要做的操作数。

    样例输入
    aa
    ab
    样例输出
    1

    代码:

    #include <stdio.h>
    int min(int x,int y,int z)
    {
        if(x>y)
            x=y;
        if(x>z)
            x=z;
        return x;
    }
    char a[1000],b[1000];
    int  dp[1000][1000]; //dp[i][j]代表a数组前i元素和b数组前j元素匹配所进行的操作数
    int main()
    {	
        scanf("%s%s",a,b);
        int len1=strlen(a);
        int len2=strlen(b);
        for(int i=1;i<=len1;i++)
            dp[i][0]=i;     //处理边界情况
        for(int j=1;j<=len2;j++)
            dp[0][j]=j;
        for(int i=1;i<=len1;i++)  //防止数组下标出现负数 故i、j从1开始处理
        {
            for(int j=1;j<=len2;j++)
            {
                if(a[i-1]==b[j-1])
                {
                    dp[i][j]=dp[i-1][j-1];
                }
                else
                {
                    dp[i][j]=min(dp[i-1][j-1],dp[i-1][j],dp[i][j-1])+1;
                }    //分别对应修改 a删除 a增加
            }
        }
        printf("%d\n",dp[len1][len2]);
        return 0;
    }
    
    展开全文
  • 最长公共子序列 编辑距离 最长递增子序列等

    会慢慢写最长公共子序列、最短编辑距离等与字符串相关的算法,其实也就是数组相关的算法...

    一、最长公共子序列

    Solve1里递推公式为:

    dp[i][j] = 0                                         if i = 0 or j  = 0

    dp[i][j] = dp[i-1][j-1]+1                       if s1[i-1] = s2[j-1]

    dp[i][j] = max{dp[i-1][j],dp[i][j-1]}       if s1[i-1] != s2[j-1]

    这里i、j并不是s1、s2中字符下标而是已经s1前i个字符与s2前j个字符,dp[i][j]表示s1前i个字符与s2前j个字符最长公共子序列

    Solve1只打印了最长公共子序列的其中一个,没有考虑多个相同的最长公共子序列

    Solve2稍微变换了递推公式:

    dp[i][j]表示s1前i+1个字符与s2前j+1个字符的最长公共子序列,没什么太大变换

    Solve3只用了一维数组,减少了空间复杂度

    Solve4是递归写法

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    //#define _DEBUG
    int max(int a, int b){
        return a>b?a:b;
    }
    
    void Solve1(char s1[], char s2[]){
        int i,j;//i、j表示扫过的s1、s2长度
        int len1 = strlen(s1);
        int len2 = strlen(s2);
        int dp[len1+1][len2+1];
    
        for(i = 0; i <= len1; ++i) dp[i][0] = 0;
        for(j = 0; j <= len2; ++j) dp[0][j] = 0;
        for(i = 1; i <= len1; ++i){
            for(j = 1; j <= len2; ++j){
                if(s1[i-1] == s2[j-1]){//是i-1 与 j-1
                    dp[i][j] = dp[i-1][j-1] + 1;
                }else{
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        #ifdef _DEBUG
        for(i = 0; i <= len1; ++i){
            for(j = 0; j<= len2; ++j){
                printf("%d ",dp[i][j]);
            }
            printf("\n");
        }
        #endif// _DEBUG
        printf("Solve1:%d\n",dp[len1][len2]);
    
        for(i = len1; i >= 0; ){
            for(j = len2; j >= 0; ){
                if(s1[i-1] == s2[j-1]){
                    printf("%c ",s1[i-1]);
                    --i;
                    --j;
                }else{
                    if(dp[i][j-1] > dp[i-1][j]){
                        --j;
                    }else{
                        --i;
                    }
                }
            }
        }
        printf("\n");
    }
    
    /*错误
    //dp[][0]与dp[0][]错误
    void Solve2(char s1[], char s2[]){
        int i,j;
        int len1 = strlen(s1);
        int len2 = strlen(s2);
        int dp[len1][len2];
    
        for(i = 0; i < len1; ++i) dp[i][0] = s1[i]==s2[0]?1:0;
        for(j = 0; j < len2; ++j) dp[0][j] = s1[0]==s2[j]?1:0;
        for(i = 1; i < len1; ++i){
            for(j = 1; j < len2; ++j){
                if(s1[i] == s2[j]){
                    dp[i][j] = dp[i-1][j-1] + 1;
                }else{
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
    
        for(i = 0; i < len1; ++i){
            for(j = 0; j< len2; ++j){
                printf("%d ",dp[i][j]);
            }
            printf("\n");
        }
        printf("%d\n",dp[len1-1][len2-1]);
    }
    */
    
    void Solve2(char s1[], char s2[]){
        int i,j;//i、j是s1 s2下标
        int len1 = strlen(s1);
        int len2 = strlen(s2);
        int dp[len1][len2];
        int dp1 = 0;
        int dp2 = 0;
        dp[0][0] = s1[0]==s2[0]?1:0;
        for(i = 1; i < len1; ++i){
            if(dp1 == 0 && s1[i] == s2[0]) dp1 = 1;
            dp[i][0] = dp1;
        }
        for(j = 1; j < len2; ++j){
            if(dp2 == 0 && s1[0] == s2[j]) dp2 = 1;
            dp[0][j] = dp2;
        }
    
        for(i = 1; i < len1; ++i){
            for(j = 1; j < len2; ++j){
                if(s1[i] == s2[j]){
                    dp[i][j] = dp[i-1][j-1] + 1;
                }else{
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        #ifdef _DEBUG
        for(i = 0; i < len1; ++i){
            for(j = 0; j< len2; ++j){
                printf("%d ",dp[i][j]);
            }
            printf("\n");
        }
        #endif // _DEBUG
        printf("Solve2:%d\n",dp[len1-1][len2-1]);
    }
    
    void Solve3(char s1[], char s2[]){
        int i,j;
        int len1 = strlen(s1);
        int len2 = strlen(s2);
        int dp[len2+1];
    
        for(j = 0; j <= len2; ++j){
            dp[j] = 0;
        }
    
        for(i = 1; i <= len1; ++i){
            for(j = 1; j <= len2; ++j){
                if(s1[i-1] == s2[j-1]){
                    dp[j] = dp[j-1] + 1;
                }else{
                    dp[j] = max(dp[j],dp[j-1]);
                }
            }
        }
        #ifdef _DEBUG
        for(j = 0; j<= len2; ++j) printf("%d ",dp[j]);
        printf("\n");
        #endif // _DEBUG
        printf("Solve3:%d\n",dp[len2]);
    }
    
    int Solve4(char s1[], char s2[], int i, int j,int dp[][20]){
        //递归求解
        if(i==0 || j==0){
            dp[i][j] = 0;
            return dp[i][j];
        }
        if(dp[i][j] != -1){
            return dp[i][j];
        }
        if(s1[i-1] == s2[j-1]){
            dp[i][j] = Solve4(s1,s2,i-1,j-1,dp)+1;
            return dp[i][j];
        }
        dp[i][j] = max(Solve4(s1,s2,i-1,j,dp),Solve4(s1,s2,i,j-1,dp));
        return dp[i][j];
    }
    
    int main(){
        char s1[20] = "abcdef";
        char s2[20] = "dgajchdef";
        int dp[20][20];
        memset(dp, -1, sizeof(dp));
        Solve1(s1,s2);
        Solve2(s1,s2);
        Solve3(s1,s2);
        printf("Solve4:%d",Solve4(s1,s2,strlen(s1),strlen(s2),dp));
        return 0;
    }
    

    二、编辑距离

    LeetCode原题:

    72. Edit Distance

    Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)

    You have the following 3 operations permitted on a word:

    a) Insert a character
    b) Delete a character
    c) Replace a character

    递推公式:

    dp[i][j]表示长度为i与长度为j的字符串的编辑距离,对应原题即word1(2)中前i(j)个字符组成的字符串:

    dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1                            if  word1[i-1] != word2[j-1]

    dp[i][j] = dp[i-1][j-1]                                                                    if  word1[i-1] == word2[j-1]

    也可以写成dp[i][j] = min(dp[i-1][j]+1,    dp[i][j-1]+1,   dp[i-1][j-1] + word1[i-1]==word2[j-1]?0:1),但这种写法感觉上似乎并不是很好,因为在word1[i-1] == word2[j-1]时这种写法还在dp[i-1][j]+1,    dp[i][j-1]+1,   dp[i-1][j-1] + word1[i-1]三者之中取最小,并没有意识到当word1[i-1] == word2[j-1]时dp[i][j] = dp[i-1][j-1]一定成立!!!

    下面是碎碎念,可忽略:

    开始想着 dp[i-1][j-1] + word1[i-1]==word2[j-1]?0:1是否一定会小于或等于dp[i-1][j]+1,    dp[i][j-1]+1,如果成立那么动态规划就可以变成贪心算法,但是并不成立,可找到反例。从下面证明中也可以得到答案。

    首先证明dp[i-1][j-1]<=dp[i-1][j]+1:

    w1'经过dp[i-1][j]步可变为w2,再删除w2末位字符得到w2',所以经过dp[i-1][j]+1不w1'可转换为w2',而dp[i-1][j-1]为w1',w2'的编辑距离,因而dp[i-1][j]+1>=dp[i-1][j-1]一定成立,所以没必要像第二种递推公式那样比较dp[i-1][j]+1,    dp[i][j-1]+1,   dp[i-1][j-1]。

    dp[i-1][j-1]<=dp[i-1][j]一定成立吗?

    dp[i-1][j-1]<dp[i-1][j]一定成立吗?

    两个答案都是不!简单的反例:word1=“A”,word2=“A”,dp[1][0]=1,dp[1][1]=0,dp[i-1][j-1]>dp[i-1][j]

    总结:dp[i-1][j-1]<=dp[i-1][j]+1并且等号可成立

    以上只是比较了dp[i-1][j-1]与dp[i-1][j]直接的大小,并未讨论dp[i][j]什么情况下与dp[i-1][j-1]相等。

    以下是正式的证明递推公式

    设字符串w1、w2长度分别为i、j,且w1、w2末位字符相同即w1[i-1]=w2[j-1],w1前i-1个字符组成的字符串为w1',w2前j-1个字符组成的字符串为w2',w1、w2编辑距离为dp[i][j],w1'与w2编辑距离为dp[i-1][j],w1'与w2'编辑距离为dp[i-1][j-1]。

    int min(int a, int b){
        return a<b?a:b;
    }
    int minDistance(char* word1, char* word2) {
        int i,j;
        int len1 = strlen(word1);
        int len2 = strlen(word2);
        int dp[len1+1][len2+1];
        for(i = 0; i <= len1; ++i) dp[i][0] = i;
        for(j = 0; j <= len2; ++j) dp[0][j] = j;
        for(i = 1; i <= len1; ++i){
            for(j = 1; j <= len2; ++j){
                if(word1[i-1] == word2[j-1]){
                    dp[i][j] = dp[i-1][j-1];
                }else{
                    dp[i][j] = min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
                }
            }
        }
        return dp[len1][len2];
    }


    展开全文
  • 最大子序列、最长递增子序列、最长公共子串、最长公共子序列、字符串编辑距离 昨天百度二面写代码的时候第一个题就是最长公共子序列问题。。。

    昨天百度二面写代码的时候第一个题就是最长公共子序列问题。。。

    展开全文
  • 找工作知识储备(2)---数组字符串那些经典算法:最大子序列和,最长递增子序列,最长公共子串,最长公共子序列,字符串编辑距离,最长不重复子串,最长回文子串 最大子序列、最长递增子序列、最长公共子串、最长...
  • 0 、前言 动态规划就是把一个大的问题拆分成几个类似的子问题,通过求解子问题来获得... 编辑距离 、最长公共子串、最长公共子序列以及最长递增子序列都是采用动态规划方法进行求解的,而且他们之间有相同和不同之处
  • 编辑距离最长公共子序列,最长公共子串,最长递增子序列 1.编辑距离 编辑距离,又称Levenshtein距离(也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将...
  • 一、最长公共子串和最长公共子序列 最长公共子串(Longest Common Substring)是串的一个连续的部分。最长公共子序列(Longest Common Subsequence,LCS)是不一定是连续的,但是不改变序列中元素在原串中的相对顺序...
  • 最长公共子序列、最长公共子串、最小编辑距离是三种常见的字符串比较算法,考虑到其中的动态规划思想、状态转移方程比较类似,实现的方法也是如出一辙,这里将其状态转移方程和相应的实现代码做一个总结。...
  • 最长递增子序列: 解法1: 很明显用动态规划的算法,选取下面的阶段(这种选法极为常见),可使阶段间的关系具有无后效性。 阶段:在所有以元素k结尾的子数组中,选出其中的最长递增子序列,k=1,2...n。 状态:...
  • 1. 如何衡量字符串的相似性如何量化两个字符串之间的相似性呢?...最长公共子序列只允许增删操作,表示两个字符串相似程度的大小。下面的例子中莱文斯坦距离为 3,最长公共子序列为 4。2. 莱文斯坦距离对于两个...
  • 最长递增子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j]...a[j],这样最长子序列称为最长递增子序列。 设dp[i]表示以i为结尾的最长递增子序列的长度,则状态转移方程为: dp[i] = max{...
  • 问题描述 最大连续子序列最长递增子序列 最长递增子序列
  • 最长公共子串、最长公共子序列、最短编辑距离等动态规划一般是O(m*n)的复杂度,还有0(2*m)的方法。 有时间自己写一个。 最大和子序列 最大和子序列是要找出由数组成的一维数组中和最大的连续子序列。比如{5,-3,4,2}...
  • 前段时间看过最长公共子序列的动态规划算法,这两天又看到了编辑距离的动态规划算法,感觉两者有相似之处,而状态转移方程又不十分直观,所以打算把其原理记录下来,以防以后忘记。

空空如也

空空如也

1 2 3 4 5 ... 17
收藏数 335
精华内容 134
关键字:

最长公共子序列编辑距离