昨晚碰巧看到腾讯2017的一道暑期实习生题目:构造回文串。题目描述如下见注释。
思路:
本来自己是一开始使用递归解决的,直接计算需要删除的字符数。做法是这样:有两个指针,一个head,一个tail。初始时head指向string的头,而tail指向string的尾。首先判断head和string的字符是否相等。如果相等,则进行head++,tail–,进行循环比较。如果不相等,那么说明这时需要删去一个字符。那么到底删去左边的字符还是右边的字符,这时就需要考虑了。所以,我的做法先删去左边一个,进行递归;接着删去右边的一个,进行递归。对应在代码中,就是head+1,而tail不变;tail-1,而head不变。通过比较左边和右边需要删除的字符数大小。选择最小的那个,然后+1进行返回。
不过,后来运行时发现递归的效率太低了,稍微长一点的字符串就不能匹配了。(其实,我也不知道这样对不对。。。)
后来,在网上看别人的解法,原来都是用了动态规划,而且这不是像我这样直接计算需要删除的字符串,而是先计算可以匹配的字符串。最后用字符串的总长度减去匹配的回文字符串长度就得到了最少需要删除的字符数。另外,使用动态规划,就不需要使用递归,大大提高了运行效率。
代码如下:
#include<iostream>
#include <algorithm>
using namespace std;
/*
给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢?
输出需要删除的字符个数。
输入描述: 输入数据有多组,每组包含一个字符串s,且保证:1<=s.length<=1000.
输出描述: 对于每组数据,输出一个整数,代表最少需要删除的字符个数。
输入例子:
abcda
google
输出例子:
2
2
*/
/*
//recursive solution
int findMinNumber(string inputString, long head, long tail) {
if (head > tail) {
return -1;
}
while (head < tail) {
if (inputString[head] == inputString[tail]) {
head++;
tail--;
} else {
break;
}
}
int leftMin = findMinNumber(inputString, head + 1, tail);
int rightMin = findMinNumber(inputString, head, tail - 1);
return leftMin > rightMin ? rightMin + 1 : leftMin + 1;
};
*/
//dynamic programming solution
int findMinNumber(string inputString, string reverseString) {
unsigned long inputStringLength = inputString.length();
long int i, j;
//record[0][0]表示了当前还没有字符匹配成功
int **record = new int *[inputStringLength + 1];
for (i = 0; i < inputStringLength + 1; i++) {
record[i] = new int[inputStringLength + 1];
for (j = 0; j < inputStringLength + 1; j++) {
record[i][j] = 0;
}
}
for (i = 1; i < inputStringLength + 1; i++) {
for (j = 1; j < inputStringLength + 1; j++) {
if (inputString[i - 1] == reverseString[j - 1]) {
//成功匹配到一个回文字符
record[i][j] = record[i - 1][j - 1] + 1;
} else {
//取前面最大成功匹配的回文字符串长度
record[i][j] = max(record[i - 1][j], record[i][j - 1]);
}
}
}
return record[inputStringLength][inputStringLength];
}
int main() {
string inputString;
while (cin >> inputString) {
string reverseString = inputString;
reverse(reverseString.begin(), reverseString.end());
long int minNumber = inputString.length() - findMinNumber(inputString, reverseString);
cout << minNumber << endl;
}
return 0;
}