leetcode *647. 回文子串(待深究马拉车算法)

【题目】*647. 回文子串

*5. 最长回文子串
*647. 回文子串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:

输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"

示例 2:

输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

提示:
输入的字符串长度不会超过 1000 。

【解题思路1】中心扩散

枚举每一个可能的回文中心,然后再判断这些子串是否是回文,用两个指针分别向左右两边拓展,当两个指针指向的元素相同的时候就拓展,否则停止拓展。
长度为 n 的字符串会生成 2n-1 组回文中心 [l, r],其中 l =⌊i/2⌋ ,r = ⌊i/2⌋ +(i mod 2)。这样只要从 0 到 2n−2 遍历 i,就可以得到所有可能的回文中心,这样就把奇数长度回文和偶数长度回文两种情况统一起来了。
2n-1 组回文中心分别是n个单字符和n - 1个双字符。

class Solution {
    
    
    public int countSubstrings(String s) {
    
    
        int n = s.length(), ans = 0;
        for (int i = 0; i < 2 * n - 1; ++i) {
    
    
            int l = i / 2, r = i / 2 + i % 2;
            while (l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
    
    
                --l;
                ++r;
                ++ans;
            }
        }
        return ans;
    }
}

【解题思路2】动态规划

dp数组: dp[i][j] 表示字符串s在[i,j]区间的子串是否是一个回文串。
状态转移方程:当 s[i] == s[j] && (j - i < 2 || dp[i + 1][j - 1]) 时,dp[i][j]=true,否则为false

  • 当只有一个字符时,比如a自然是一个回文串。
  • 当有两个字符时,如果是相等的,比如aa,也是一个回文串。
  • 当有三个及以上字符时,比如ababa这个字符记作串1,把两边的a去掉,也就是bab记作串2,可以看出只要串2是一个回文串,那么左右各多了一个a的串1必定也是回文串。所以当s[i]==s[j]时,自然要看dp[i+1][j-1]是不是一个回文串。
class Solution {
    
    
    public int countSubstrings(String s) {
    
    
        // 动态规划法
        boolean[][] dp = new boolean[s.length()][s.length()];
        int ans = 0;

        for (int j = 0; j < s.length(); j++) {
    
    
            for (int i = 0; i <= j; i++) {
    
    
                if (s.charAt(i) == s.charAt(j) && (j - i < 2 || dp[i + 1][j - 1])) {
    
    
                    dp[i][j] = true;
                    ans++;
                }
            }
        }

        return ans;
    }
}

【解题思路3】Manacher 马拉车算法(待研究)

class Solution {
    
    
    public int countSubstrings(String s) {
    
    
        int n = s.length();
        StringBuffer t = new StringBuffer("$#");
        for (int i = 0; i < n; ++i) {
    
    
            t.append(s.charAt(i));
            t.append('#');
        }
        n = t.length();
        t.append('!');

        int[] f = new int[n];
        int iMax = 0, rMax = 0, ans = 0;
        for (int i = 1; i < n; ++i) {
    
    
            // 初始化 f[i]
            f[i] = i <= rMax ? Math.min(rMax - i + 1, f[2 * iMax - i]) : 1;
            // 中心拓展
            while (t.charAt(i + f[i]) == t.charAt(i - f[i])) {
    
    
                ++f[i];
            }
            // 动态维护 iMax 和 rMax
            if (i + f[i] - 1 > rMax) {
    
    
                iMax = i;
                rMax = i + f[i] - 1;
            }
            // 统计答案, 当前贡献为 (f[i] - 1) / 2 上取整
            ans += f[i] / 2;
        }

        return ans;
    }
}

猜你喜欢

转载自blog.csdn.net/XunCiy/article/details/108092700
今日推荐