题目描述
定义“平方字符串”,意思为一个字符串s[1…n],其中子串s1[1…k]和s2[k+1…n]完全相等。例如”abab”,”aaaa”是平方字符串,”aba”,”abca”就不是
有如下几种操作:
1. 将任意一个字符替换成另外一个字符
2. 可以在字符串的任意一个地方插入一个字符
3. 可以删去字符串的任意一个字符
给一个初始字符串,问经过上述操作最少几次能够将该字符串变成“平方字符串”。
输入格式
第一行输入一个数T,表示测试数据个数,对于每个测试数据输入一个字符串,表示初始的字符串,字符串长度<=100
输出格式
对于每个测试数据输出一个数,表示最少操作次数。
输入样例
3
abcdabcmd
abcdabyd
abcdabcd
输出样例
1
1
0
解题思路
看到题目的前半部分,将一个字符串分成两个子串分别为s1[1…k]和s2[k+1…n],很容易想到就是区间dp的模板,因为这边只能分成两个区间,所以我们只要枚举分隔区间的区间线k就可以了,k的范围应该为:
边界就是字符串的开头和结尾
即:0~strlen(s-1)
我们可以写一个循环枚举这个k,那么所有可能的区间我们就都找到了,接下来就是分别求出它们变成相等字符串的最小操作数量,然后再所有可能性里面求出最小值即可。
之后我们发现,题目中的几种操作:替换,增加,删除。这些不就是最短编辑距离的模板题吗?
模板如下:
设dp[i][j]是字符串1到i号位置,字符串2到j号位置时的最短编辑距离
分开讨论这几种操作:
如果当前比对的两个元素相同,则不需要进行任何操作,所以状态转移方程就为
dp[i][j]=dp[i-1][j-1]
//当前的两个字母相同,不需要任何操作
//dp[i][j]的最小操作数量即为两个字符串去掉当前的字符---->dp[i-1][j-1]
如果不相同,我们则有三种操作方式,插入,删除,修改
如果是插入:
如果在i这个数组插入,是和j的当前元素抵消了,所以去掉j当前元素
dp[i][j-1]+1
如果在j这个数组插入,是和i的当前元素抵消了,所以i当前元素
dp[i-1][j]+1
如果是删除:
同理可得是和插入一样的
dp[i][j-1]+1
dp[i-1][j]+1
如果是修改
那么不论是修改哪个字符串的,当前的两个元素都会抵消,所以最小编辑距离就是都去掉一个元素之后的最小编辑距离加1
dp[i-1][j-1]+1
最后几种操作比较一下,取最小值就可以
同时给一下初始值
当某一个字符串为空的时候,另一个字符串有长度,那么最短编辑距离就为另一个字符串的长度
例:
第一个字符串:(空)
第二个字符串:abc
那么此时最短编辑距离就为3,其实就是进行三次插入操作
所以初始值为:
for(int i=1;i<=l1;i++)dp[i][0]=i;
for(int i=1;i<=l2;i++)dp[0][i]=i;
代码参考
代码编写思路如下:
输入目标字符串,并获取它的长度
对分隔字符串的分割线k进行循环枚举
根据k将分隔出的两个字符串分别放入两个字符数组中,并分别求出它们的长度
双重循环开始动态规划
比对当前元素是否相同,如果相同,则不需要操作,即:dp[i][j]=dp[i-1][j-1]
否则则使用一次替换操作:dp[i][j]=dp[i-1][j-1]+1
接着分别比对删除,替换,插入(前文提到)三种操作哪需要的编辑距离比较小
最后将这次的编辑距离和之前的最短编辑距离进行比对,更新最小值
输出最短编辑距离
#include<bits/stdc++.h>
using namespace std;
char s1[1000],s2[1000],s[1000];
int dp[500][500],l1,l2,max1;
int minn(int a,int b,int c)
{
int minx=min(a,b);
minx=min(minx,c);
return minx;
}
int main()
{
int fmin=0x3f3f3f3f,t;
cin>>t;
while(t--)
{
fmin=0x3f3f3f3f;
cin>>s;
int l=strlen(s);
for(int k=0;k<=l-1;k++)
{
memset(s1,0,sizeof(s1));
memset(s2,0,sizeof(s2));
memset(dp,0,sizeof(dp));
for(int i=0;i<=k;i++)s1[i]=s[i];//根据分割线k分别存储两个字符串
for(int i=k+1,j=0;i<=l-1;i++,j++)s2[j]=s[i];
l1=strlen(s1);
l2=strlen(s2);
for(int i=1;i<=l1;i++)dp[i][0]=i;//给出dp初始值
for(int i=1;i<=l2;i++)dp[0][i]=i;
for(int i=1;i<=l1;i++)//双重循环开始动态规划
{
for(int j=1;j<=l2;j++)
{
if(s1[i-1]==s2[j-1]){dp[i][j]=dp[i-1][j-1];}//如果当前字符相同
else{dp[i][j]=dp[i-1][j-1]+1;}//不同则使用替换操作
dp[i][j]=minn(dp[i-1][j]+1,dp[i][j-1]+1,dp[i][j]);
//插入,删除,替换比较出最小值
}
}
fmin=min(fmin,dp[l1][l2]);
//这次求出来的最小值和总最小值进行比较,如果更小,则更新最小值
}
cout<<fmin<<endl;
}
}