字符串编辑距离算法(hdu 4271 hdu 4323 hdu 3540)

关于字符串子序列的一些总结:http://www.cnblogs.com/zhangchaoyang/articles/2012070.html

                                                 http://www.cnblogs.com/ziyi--caolu/p/3235229.html

字符串编辑距离的意思就是给你两个串s1,s2,可以增加字符,删除字符,改变字符,求一个串转变为另一个串的最小次数

算法解说介绍好的博客推荐:http://www.cnblogs.com/biyeymyhjob/archive/2012/09/28/2707343.html

假设把s1变成s2,总而言之就是dp[i][j]=min(dp[i][j-1]+1,dp[i-1][j]+1,dp[i-1][j-1]+f(i,j));其中f(i,j)为1(s1[i]!=s2[j]),0(s1[i]==s2[j])

其实dp[i][j-1]+1可以理解为进行在一次增操作,dp[i-1][j]+1可以理解成进行了一次减操作,dp[i-1][j-1]+f 则理解成进行改操作

对于初始化时,当的d[0][j]=j;d[i][0]=i;适合将s1完全转变为s2,dp[0][j]=0,dp[i][0]=i适合把s1中的子串转化成s2,dp[0][j]=j,dp[i][0]=i适合把s1转化成s2中的子串

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4323

题意:输入一堆数字(不超过10位) 进行m次查询,每次查询都包括两个数字 前面数字是转变的串 后面那个允许进行的最大转变次数 输入满足次数的数的数量,可以进行增删改

题解:对上面的算法的裸应用

代码:

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;
int dp[2][12];
char father[1505][15];
int str[1505];
char son[15];
int findSmall(int n,int slen,int d)
{
    int sum=0;
    for(int i=1;i<=n;i++)
    {
        for(int k=0;k<=str[i];k++)
        {
            dp[0][k]=k;
        }
        for(int j=1;j<=slen;j++)
        {
            dp[j%2][0]=j;
            for(int k=1;k<=str[i];k++)
            {
                dp[j%2][k]=min(dp[(j-1)%2][k-1]+(son[j-1]!=father[i][k-1]),min(dp[j%2][k-1],dp[(j-1)%2][k])+1);
            }
        }
        if(dp[slen%2][str[i]]<=d)
        {
            sum++;
        }
    }
    return sum;
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        printf("Case #%d:\n",i);
        int n,m;
        scanf("%d %d",&n,&m);
        getchar();
        for(int j=1;j<=n;j++)
        {
            scanf("%s",father[j]);
            str[j]=strlen(father[j]);
        }
        while(m--)
        {
            int d;
            scanf("%s %d",son,&d);
            int temp=findSmall(n,strlen(son),d);
            printf("%d\n",temp);
        }

    }
    return 0;
}



题目:http://acm.hdu.edu.cn/showproblem.php?pid=4271

题意:输入一个母字符串(环),然后输入n个子字符串,要求输出n个子字符串中能转换成母字符串子串(连续)的且转换次数最小的串及转换次数

题解:要考虑环,就是考虑把最前面的10个(子串最大为10个字符)复制到母串的后面,当母串比子串短的时候 枚举来求转换次数,当长时,通过上面的方法进行,只是初始赋值的有所不同,其他与最初的算法相同

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define MAX 100500

using namespace std;
char mains[MAX];
int dp[12][MAX];
char sons[12][20];
int findSmall(char *son,char *father,int slen,int flen)
{
    int anst=MAX;
    for(int i=1;i<=slen;i++)
    {
        dp[i][0]=i;
        for(int j=1;j<=flen;j++)
        {
            dp[i][j]=min(dp[i-1][j-1]+(father[j-1]!=son[i-1]),min(dp[i-1][j],dp[i][j-1])+1);
            if(i==slen&&anst>dp[i][j])
            {
                anst=dp[i][j];
            }
        }
    }
    return anst;
}
int main()
{
    int n;
    while(~scanf("%s",mains))
    {
        memset(dp,0,sizeof(dp));
        int len=strlen(mains);
        int ans=MAX;
        int pos=0;
        for(int i=len,j=0;j<=10;j++){mains[i+j]=mains[j];}
        mains[len+10]='\0';
        scanf("%d",&n);
        getchar();
        for(int i=0;i<n;i++)
        {
            int slen;
            int test=MAX;
            scanf("%s",sons[i]);
            slen=strlen(sons[i]);
            if(slen>len)
            {
                for(int j=0;j<len;j++){test=min(test,findSmall(sons[i],mains+j,slen,len));}
            }
            else
            {
                test=min(test,findSmall(sons[i],mains,slen,len+min(10,len)));
            }
            if(test<ans||(test==ans&&(strcmp(sons[pos],sons[i])>0)))
            {
                ans=test;pos=i;
            }
        }
        printf("%s %d\n",sons[pos],ans);
    }
    return 0;
}

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3540

题意:输入两个字符串A、B及一个整数LIM,问是否存在将B在LIM次数内转化为A的子串,存在输出最小的转换次数,不存在则输出-1

题解:与上题差不多,就是要考虑时间复杂度的问题,所以只能利用LIM进行优化,相当于每次以A[i]结尾,相邻dp[i][j]和dp[i][j-1]或dp[i-1][j]之间最多相差1,则考虑刚开始以LIM的长度进行维护,每次加一,保证最大的dp值不大于LIM,因为B作为j上面的值,必须保证达到B的长度,所以此方法可行,每次维护当dp[i][last]>LIM时进行减少last操作,当last大于或等于B串长度时开始记录次数,返回最小次数

代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define FMAX 1000050
#define SMAX 1050
using namespace std;

int dp[2][10050];
int findSmall(char *sons,char *fathers,int flen,int slen,int k)
{
    int temp=FMAX;
    int last=min(k,slen);
    for(int i=1;i<=min(k+1,slen);i++)dp[0][i]=i;
    for(int i=1;i<=flen;i++)
    {
        int j=1;
        for(;j<=last;j++)
        {
            dp[i%2][j]=min(dp[(i-1)%2][j-1]+(sons[i-1]!=fathers[j-1]),min(dp[i%2][j-1],dp[(i-1)%2][j])+1);
        }
        dp[i%2][j]=min(dp[(i-1)%2][j-1]+(sons[i-1]!=fathers[j-1]),dp[i%2][j-1]+1);
        if(last>=slen)
        {
            temp=min(temp,dp[i%2][slen]);
        }
        last=min(last+1,slen);
        while(dp[i%2][last]>k)
        {
            last--;
        }
    }
    return temp;
}
int main()
{
    int ans;
    char fathers[FMAX];
    char sons[SMAX];
    while(~scanf("%s%s",fathers,sons))
    {
        int flen=strlen(fathers);
        int slen=strlen(sons);
        scanf("%d",&ans);
        int temp=findSmall(sons,fathers,flen,slen,ans);
        if(temp>ans){printf("-1\n");}
        else{printf("%d\n",temp);}
    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值