平方字符串(最短编辑距离)(区间dp)(动态规划)

题目描述

定义“平方字符串”,意思为一个字符串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;
	}
}

关联题目

edit(最短编辑距离)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水轻侮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值