回文串问题与动态规划

回文串的算法问题几乎都是用动态规划解决的。

5. Longest Palindromic Substring
算法一:从对称中心扩展,算法时间复杂度 O ( N 2 ) O(N^2) ,空间复杂度为 O ( 1 ) O(1) ;

class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        if(n==0)
            return "";
        int start = 0, end = 0;
        for (int i = 0; i < 2 * n - 1; i++) {
            int left = i / 2;
            int right = left + i % 2;
            int len=expand(s,left,right);
            System.out.println(len);
            if(len>end-start+1){
                if(left==right){
                    start=left-len/2;
                    end=left+len/2;
                }else
                {
                    start=left-len/2+1;
                    end=right+len/2-1;
                }
            }
        }
        return s.substring(start,end+1);
    }

    public static int expand(String s, int left,int right) {

        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        return right - left - 1;
    }
}

上述算法从对称中心扩展得到的回文串长度计算right - left - 1。
算法二:动态规划,时间复杂度和空间复杂度都为 O ( N 2 ) O(N^2)
dp[i][j]表示子串s[i…j]是否为回文串

public String longestPalindrome(String s) {

        int n = s.length();
        boolean[][] dp = new boolean[n][n];

        int start = 0, end = 0;
        for (int i = n - 1; i >= 0; i--) {

            for (int j = i; i < n; j++) {

                dp[i][j] = (s.charAt(i) == s.charAt(j)) && (j - i <= 2 || dp[i + 1][j - 1]);
                if (dp[i][j] && j - i + 1 > end - start) {
                    start = i;
                    end = j;
                }
            }
        }
        return s.substring(start, end + 1);
    }

算法三:Manacher’s Algorithm
时间和空间复杂度都为 O ( N ) O(N)

class Solution {
    public String longestPalindrome(String s) {
        if(s.length()==0)
            return s;
        StringBuilder sb = new StringBuilder();
        sb.append('^');
        for (int i = 0; i < s.length(); i++)
        {
            sb.append('#');
            sb.append(s.charAt(i));
        }
        sb.append("#$");
        String preString = sb.toString();

        int n = preString.length();
        int[] P = new int[n];
        int C = 0, R = 0;
        for (int i = 1; i < n - 1; i++) {
            int mirror = 2 * C - i;
            if (i < R)
                P[i] = Math.min(R - i, P[mirror]);
            else
                P[i] = 0;
            while (preString.charAt(i + P[i] + 1) == preString.charAt(i - P[i] - 1))
                P[i]++;
            if (P[i] + i > R) {
                C = i;
                R = i + P[i];
            }
        }
        int maxLen = 0, centre = 0;
        for (int i = 1; i < n - 1; i++) {
            if (maxLen < P[i]) {
                maxLen = P[i];
                centre = i;
            }
        }
        int start = (centre- 1 -maxLen)/ 2 ;
        return s.substring(start, start+maxLen);
    }
}

516. Longest Palindromic Subsequence
算法一:暴力递归

public class LongestPalindromicSubsequence {

    public Integer[][] memo;

    public int longestPalindromeSubseq(String s) {
        if (s.length() == 0)
            return 0;
        memo = new Integer[s.length()][s.length()];
        return recursive(0, s.length() - 1, s);
    }

    public int recursive(int i, int j, String s) {
        if (i > j)
            return 0;
        if (i == j)
            return 1;
        if (memo[i][j] != null)
            return memo[i][j];
        if (s.charAt(i) == s.charAt(j))
            memo[i][j] = 2 + memo[i + 1][j - 1];
        else
            memo[i][j] = Math.max(recursive(i, j - 1, s), recursive(i + 1, j, s));
        return memo[i][j];
    }
}

算法二:二维动态规划
时间和空间复杂度都为 O ( N 2 ) O(N^2)

public static int longestPalindromeSubseq(String s) {
        int n = s.length();
        int[][] dp = new int[n][n];
        for (int i = 0; i < n; i++)
            dp[i][i] = 1;
        for (int len = 2; len <= n; len++) {
            for (int start = 0; start <=n - len; start++) {
                if (s.charAt(start) == s.charAt(start + len - 1))
                    dp[start][start + len - 1] = dp[start + 1][start + len - 2] + 2;
                else
                    dp[start][start + len - 1] = Math.max(dp[start + 1][start + len - 1], dp[start][start + len - 2]);
            }
        }
        return dp[0][n - 1];
    }

算法三:一维动态规划
时间复杂度 O ( N 2 ) O(N^2) ,空间复杂度为 O ( N ) O(N)

class Solution {
    public static int longestPalindromeSubseq(String s) {
        int[] preRow = new int[s.length()];
        
        for (int i = s.length() - 1; i >= 0; i--) {
            int[] curRow = new int[s.length()];
            curRow[i] = 1;
            for (int j = i + 1; j < s.length(); j++) {
                int left = curRow[j - 1];
                int bottom = preRow[j];
                int leftBottom = preRow[j - 1];
                if (s.charAt(i) == s.charAt(j))
                    curRow[j] = leftBottom + 2;
                else
                    curRow[j] = Math.max(left, bottom);
            }
            preRow = curRow;
        }
        return preRow[s.length() - 1];
    }
}

647. Palindromic Substrings
算法一:从对称中心扩展

class Solution {
    public int countSubstrings(String s) {

        int N = s.length(), ret = 0;
        for (int center = 0; center < 2 * N - 1; center++)
        {
            
            /*
            centre的位置既可以是字符串某个字母(回文串长度为奇数,起始left=right),也可以是间隔(回文串的长度为偶数,起始left+1=right)
            012345678
            m|b|a|b|m  N-1+N个位置
             */
            int left = center / 2;
            int right = left + center % 2;
            while (left >= 0 && right < N && s.charAt(left) == s.charAt(right))
            {
                ret++;
                left--;
                right++;
            }
        }
        return ret;
    }
}

算法二:Manacher’s Algorithm

class Solution {
    
    public int countSubstrings(String s) {
        
        StringBuilder sb = new StringBuilder();
        sb.append('^');
        for (int i = 0; i < s.length(); i++) {
            sb.append('#');
            sb.append(s.charAt(i));
        }
        sb.append("#$");
        String preString = sb.toString();

        int n = preString.length();
        int[] P = new int[n];
        int C = 0, R = 0;
        for (int i = 1; i < n - 1; i++) {
            int mirror = 2 * C - i;
            if (i < R)
                P[i] = Math.min(R - i, P[mirror]);
            else
                P[i] = 0;
            while (preString.charAt(i + P[i] + 1) == preString.charAt(i - P[i] - 1))
                P[i]++;
            if (P[i] + i > R) {
                C = i;
                R = i + P[i];
            }
        }
        int ans=0;
        for(int v:P)
            ans+=(v+1)/2;
        return ans;
    }
}

猜你喜欢

转载自blog.csdn.net/To_be_to_thought/article/details/89924682