关于字符串子序列的一些总结: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;
}