zoukankan      html  css  js  c++  java
  • 关于回文字符串的一些常见问题和解法

    问题1 --- 字符串的最长回文子串

    思路

      回文串是有中心的,选择一个位置作为回文串的中心,然后向两边扩展,便可以找到以该点作为中心的最长回文串,然后选择下一个位置作为中心,重复上述步骤,即可找到最长回文子串。由于回文串的中心不一定是某个字符,比如aa的中心是a#a中的#,因此可以进行字符串的扩展映射,将abcd扩展为#a#b#c#d,当然这并不是真的进行扩展,只是在运算时,将偶数下标当作#,奇数下标/2用来取值,扩展的目的是方便取中心位置。

    代码

    string longestPalindrome(string s) {
            int max = 0;
            int len;
            int index = 0;
            int size = s.size();
            for(int i = 1; i < 2*size; i++){
                len = i%2 + 1;
                while((i+len) < 2*size && (i-len) >= 0)
                    if(s[(i+len)/2] == s[(i-len)/2])
                        len += 2;
                    else
                        break;
                len --;
                if(max < len){
                    max = len;
                    index = i;
                }
                if((max+1)/2 >= (size - i/2))
                    break;
            }
            return s.substr(index/2-max/2,max);
    }

    问题2 -- 回文子串个数

    思路

      同样使用问题1的中心扩展思想,扩展一次则个数加1

    代码

    int countSubstrings(string s) {
            int len = s.size();
            int res = 0;
            int r;
            for(int i = 1; i < 2*len; i++){
                if(i%2) res++;
                r = 1;
                while(!((i+r)%2) || (i-r >= 0 && i+r < 2*len+1 && s[(i+r)/2] == s[(i-r)/2])) {
                    if((i+r)%2) res++;;
                    r++;
                }
            }
            return res;
    }

    问题3 -- 分割回文串

      对于一个字符串s,将其分割为回文串,返回可能的分割方案。例:s = "aba",则返回[  ["a","b","a"], ["aba"] ]; s = "aab",返回 [ ["a","a","b"], ["aa", "b"] ]。

    思路

      显然,本题可以通过深度优先搜索求解,搜索左边第一个可以被分割的位置,然后搜索剩余未被分割的部分即可。问题在于如何判断是否可以从某个位置分割,这要求我们验证该段是否是回文串,是才能分割该位置,因此,需要首先用动态规划将任意字串是否为回文串保存在dp[len][len]数组中,这样在DFS时直接拿出来用就好了。

      此外,在进行深搜时,会出现重复搜索的情况,若想避免重复的递归调用,需要耗费内存去保存被搜索过的状态的分割方案,这样下次搜索到该状态时,不需要递归下去,直接返回分割方案即可,然而在本题中,为了避免重复搜索而使用额外内存记录状态,并不一定能优化算法,实际上,不避免重复搜索的方法效率更高,可能是记录并返回状态信息耗费了大量时间。

    代码1 : 不考虑重复搜索

    vector<vector<string>> res;
        vector<vector<string>> partition(string s) {
            int len = s.size();
            if(!len) return res;
            vector<vector<bool>> pt(len, vector<bool>(len, false));
            pt[0][0] = true;
            for(int i = 1; i < len; i++){
                pt[i][i] = true;
                if(s[i] == s[i-1]) pt[i-1][i] = true;
            }
            for(int i = 2; i < len; i++)
                for(int j = 0; j < len-i; j++)
                    pt[j][j+i] = s[j] == s[j+i] && pt[j+1][j+i-1];
            vector<string> ans;
            dfs(s, ans, 0, pt);
            return res;
        }
        void dfs(string& s, vector<string>& ans, int k, vector<vector<bool>>& pt){
            if(k == s.size()) {
                res.push_back(ans);
                return;
            }
            for(int i = k; i < s.size(); i++){
                if(pt[k][i]) {
                    ans.push_back(s.substr(k, i-k+1));
                    dfs(s, ans, i+1, pt);
                    ans.pop_back();
                }
            }
    }

    运行结果:

     

    代码2:记录搜索状态,避免重复搜索

    map<int, vector<vector<string>>> res;
        vector<vector<string>> partition(string s) {
            int len = s.size();
            vector<vector<bool>> pt(len, vector<bool>(len, false));
            pt[0][0] = true;
            for(int i = 1; i < len; i++){
                pt[i][i] = true;
                if(s[i] == s[i-1]) pt[i-1][i] = true;
            }
            for(int i = 2; i < len; i++)
                for(int j = 0; j < len-i; j++)
                    pt[j][j+i] = s[j] == s[j+i] && pt[j+1][j+i-1];
            return dfs(s, 0, pt);
        }
        vector<vector<string>>  dfs(string& s, int k, vector<vector<bool>>& pt){
            if(res.count(k))
                return res[k];
            vector<vector<string>> ans;
            if(k==s.size()) {
                vector<string> t;
                ans.push_back(t);
                res[k] = ans;
                return ans;
            }
            for(int i = k; i < s.size(); i++){
                if(pt[k][i]) {
                    for(auto sp:dfs(s,i+1,pt)){
                        vector<string> split;
                        split.push_back(s.substr(k, i-k+1));
                        split.insert(split.end(), sp.begin(), sp.end());
                        ans.push_back(split);
                    }
                }
            }
            res[k] = ans;
            return ans;
    }

    结果如图:

     问题4 -- 分割回文串2 

      将一个字符串进行分割,得到的子串都是回文串,求满足要求的最少分割次数。

    思路

      两次动态规划结合,首先动态规划求出不同子串是否为回文串(同上题),保存在Dp[i][j]中,然后用动态规划求出前k个字符的最少分割次数,状态方程为:

    dp[k] = min(dp[i]) + 1,  0<= i < k  && Dp[i+1][k] == true

    代码

    int minCut(string s) {
            int len = s.size();
            if(len < 2) return 0;
            bool pt[len][len];
            int pt2[len] = {0};
            pt[0][0] = true;
            for(int i = 1; i < len; i++){
                pt[i][i] = true;
                pt[i-1][i] = s[i-1] == s[i];
            }
            for(int i = 2; i < len; i++)
                for(int j = 0; j < len - i; j++)
                    pt[j][j+i] = s[j] == s[j+i] && pt[j+1][j+i-1];
            for(int i = 1; i < len; i++){
                if(pt[0][i]) {
                    pt2[i] = 0;
                    continue;
                }
                int j = i;
                int m = pt2[j-1];
                while(--j > 0){
                    if(!pt[j][i])
                        continue;
                    m = min(pt2[j-1], m);
                }
                pt2[i] = m + 1;
            }
            return pt2[len-1];
    }

     

  • 相关阅读:
    ssh 免密
    SCALA XML pattern attrbute(属性)
    数据库分区的概念
    Intellij IDEA 快捷键整理
    笔记--Linux
    netstat
    笔记--MySQL相关操作
    ip地址
    使用ASMCMD管理Oracle ASM
    使用RMAN执行Oracle ASM数据迁移
  • 原文地址:https://www.cnblogs.com/zz-zhang/p/12436830.html
Copyright © 2011-2022 走看看