对称字符串问题(详细解答!)
对称字符串问题(详细解答!)
本题原题目来自于蓝桥杯的“密码脱落”问题,想见原题请自行搜索。
在此概括一下题目大意:给出一个字符串,求最多添加多少个字符,能让该字符串变为对称字符串。
解题主要思路
我们首先从该字符串的两端开始,依次比较。如果发现相等,皆大欢喜,两个箭头都往中间移动一位。
如果发现不等,这时候的问题就出现了。我们该从哪边补全字符串,才能使添加的字符串最少呢?解决这个问题的办法就是——都试一下,哪个最小就走哪边。
我们就拿上图的数字作为例子
① 在右边添加一个和左边指针一样的值
然后再将两个指针同时向中间移动一步,比较下一个值
但是在实际算法中,我们想要实现这个过程却非常简单,只需要左边指针移动,右边指针不移动就能实现这个效果。每继续这么一个操作,我们就记录一下,添加了一个字符串。这时,我们已经添加了1
个字符了
然后再继续比较,就会发现已经是个对称字符串了。
② 在左边添加一个和右边指针一样的值
然后再将两个指针同时向中间移动一步,比较下一个值。
同理,我们实现算法时,只需要将右边指针移动,左边指针不移动就行了。
同理,每进行这么一个操作,我们记录一下,已经添加了1个字符了
当我们在继续比较的时候发现这两个字符还不相等。
于是我们再分两种情况,从左边添加字符,和从右边添加字符,发现都要添加一个字符才能满足对称,因此,这时我们已经添加了2
个字符了。
③ 取①,②中值最小的
则发现最少需要添加一个字符,就能让它变为对称字符串
总结
大家可以发现这是一个递归的操作,逐次比较,发现相同就同时向中间移动一位,不同就分两种情况讨论,取最小的。
代码
相信大家根据以上的描述,应该已经会自己完成代码部分了。但是在这里,我们介绍一种用二维数组的方法来解决这个问题。
先砸出代码
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windef.h>
#include <string.h>
#define Max 1000
int main()
{
char arr1[Max];
char arr2[Max];
char arr[Max][Max];
int i, len, j = 0;
gets(arr1);
len = strlen(arr1);
for(i = len-1; i >= 0; i--, j++)
{
arr2[j] = arr1[i];
}
arr2[len] = '\0';
for(i = 0; i <= len; i++)
{
arr[i][0] = 0;
}
for(j = 0; j <= len; j++)
{
arr[0][j] = 0;
}
for(i = 1; i <= len; i++)
{
for(j = 1; j <= len;j ++)
{
if(arr1[i-1] == arr2[j-1])
{
arr[i][j] = arr[i-1][j-1] + 1;
}
else
{
arr[i][j] = max(arr[i-1][j] , arr[i][j-1]);
}
}
}
printf("%d\n", len-arr[len][len]);
return 0;
}
代码讲解
在代码中,我们定义了两个字符串,分别用来存储这个字符串和这个字符串倒过来的样子,和一个二维数组(本来应该定义为int类型的更好理解,但是int会溢出,char不会,char其实也可以用来存储数字)
随后我们观察一下这个二维数组存储值的规律:arr[ i ] [ j ]存储的值取决于这个字符串第i个值和倒数第j个值是不是相等,如若相等,这个值就等于arr[ i-1 ] [ j-1 ]的值+1,如果不等,就等于arr[ i-1 ][ j ]和arr[ i ][ j-1 ]中较大的那个。先不用理解,记住就好了。
我们先尝试一下输入ABCA字符串,这个二维数组的内容是什么。
我们乍一眼看过去会很难发现这个二维数组的规律。我们先斜着观察,假如这个字符串的值是ABBA,那么最中间的那条斜着的值应该是1234,而不是1123。
为什么ABCA中是1123呢?
因为在比较第二个值和倒数第二个值的时候(也就是B和C),发现这两个值不相等了,就将此处的值保持不变(因为最后的结果是用len 减去这个值,所以这个值越小,需要添加的字符就越多)
这时候我们再观察arr[3][2]和arr[2][3],发现两个字符相等,于是就都+1。然后在arr[3][3]的时候再取最大的值(和前面讲到的取①②最小的是一个道理)。然后再继续比较,直到最后得出答案。
上一篇: Git本地仓库基本操作及技巧
下一篇: Object.assign()