一道关于回文的笔试题

近日参加了一家国内某知名互联网公司的在线笔试,有一道算法题比较有意思,拿出来分享一下。

题目:

给定一个字符串,可以随意删除字符串里的任何字符(也可以不删除),目标是删除一些字符后让剩下的字符能够组成回文,并返回该输入字符串所能构成的最长回文长度。

例如,输入:"eabbfa"。

删除e和f后,构成"abba",因此返回4。

提示:

先看一个递归的方法:

int recursive(string& s, int start, int end) {
    if (start == end) {
        return 1;
    }
    if (start > end) {
        return 0;
    }
    if (s[start] == s[end]) {
        return recursive(s, start + 1, end - 1) + 2;
    }
    else {
        return max(recursive(s, start, end - 1), recursive(s, start + 1, end));
    }
}

int longestPalindrom(string s) {
    return recursive(s, 0, s.length() - 1);
}

但是递归方法会计算许多重复子问题,导致效率下降。

这题可以用动态规划来做。申请一个二维数组,dp[i][j],代表了字符串从 i 到 j 位置的子串中能够包含的最长回文长度。

显然,这里的起始条件是dp[i][i] = 1,i取值为[0, s.length() - 1],因为只有一个字符的字符串一定是回文。

之后可以用 k 代表 i 和 j 的距离,从1开始递增。

当s[i] == s[j]时,进行状态转移,返回dp[i][j] = max(dp[i + 1][j - 1] + 2, max(dp[i + 1][j] + 1, dp[i][j - 1] + 1));

大致思路就是这样。

代码:

int longestPalindrom(string s) {
    int len = s.length();
    if (len <= 1) {
        return len;
    }
    vector<vector<int>> dp(len, vector<int>(len, 0));
    int max_len = 1;
    // single character is palindrom
    for (int i = 0; i < len; ++i) {
        dp[i][i] = 1;
    }
    // k is distance between i and j
    for (int k = 1; k < len; ++k) {
        for (int i = 0; i < len - k; ++i) {
            int j = i + k;
            if (s[i] == s[j]) {
                dp[i][j] = max(dp[i + 1][j - 1] + 2, max(dp[i + 1][j] + 1, dp[i][j - 1] + 1));
            }
            else {
                dp[i][j] = max(dp[i + 1][j - 1], max(dp[i + 1][j], dp[i][j - 1]));
            }
        }
    }
    return dp[0][len-1];
}

int main()
{
    string s = "rdgsaraytbbdrcdfcbsbaaoio";
    int res = longestPalindrom(s);
    return 0;
}

如果有更好的思路或者发现代码有纰漏,欢迎留言指正。

原文地址:https://www.cnblogs.com/jdneo/p/5351648.html