• LeetCode 647. Palindromic Substrings的三种解法


    转载地址

    https://www.cnblogs.com/AlvinZH/p/8527668.html#_label5


    题目详情

    给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

    具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。

    示例 1:

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

    示例 2:

    输入: "aaa"
    输出: 6
    说明: 6个回文子串: "a", "a", "a", "aa", "aa", "aaa".
    

    注意:

    1. 输入的字符串长度不会超过1000。

    题目分析

    一个小问题,子串(Substring)、子数组(Subarray)和子序列(Subsequence)的区别:子串和子数组是等同的,特点是连续的,比如[1,2,3]的子串有(1), (2), (3), (1,2), (2,3), (1,2,3)。而子序列不一定相邻,但相对顺序一致,比如(1,3)是[1,2,3]的一个子序列。

    方法有很多种,简单讲一些。


    方法一: DP

    一开始定义DP[i][j]为i、j之间的回文子串数,很是麻烦,还需要另外的数组记录子串[i, j]是否是回文的。其实没有必要,直接将DP[i][j]定义成子串[i, j]是否是回文串。外循环 i从 n−1 往 0 遍历,内循环 j 从 i 往 n−1 遍历,若s[i]==s[j]:

    若i==j,则dp[i][j]=true;
    若i和j是相邻的,则dp[i][j]=true;
    若i和j中间只有一个字符,则dp[i][j]=true;
    否则,检查dp[i+1][j-1]是否为true,若为true,那么dp[i][j]就是true。
    前三条可以合并,即 j−i≤2。求得dp[i][j]真值后,如果其为true,最终结果res++。

    时间复杂度:O(n^2)。

    方法一参考代码:

    class Solution {
    public:
        int countSubstrings(string s) {
            int len = s.size(), res = 0;
            vector<vector<bool>> dp(len, vector<bool>(len, false));
            for (int i = len - 1; i >= 0; --i) {
                for (int j = i; j < len; ++j) {
                    dp[i][j] = (s[i] == s[j]) && (j - i <= 2 || dp[i + 1][j - 1]);
                    if (dp[i][j]) ++res;
                }
            }
            return res;
        }
    };
    

    方法二:回文中心法

    本题可以不用DP,而是采用一种巧妙的方法:回文中心法。什么意思呢?考虑不同的回文中心,往两边扩散,求得回文数。需要考虑两种情况:如果是奇数长度回文串,了么回文中心为最中间的一个字符;如果是偶数长度回文串,这回文中心为最中间的两个字符。

    每个回文子串只有一个回文中心,所以这种方法不会重复计算,也不会漏算。

    时间复杂度:O(n^2)。

    方法二参考代码:

    class Solution {
    public:
        int countSubstrings(string s) {
            int len = s.size(), res = 0;
            for (int i = 0; i < len; ++i) {
                int mid1 = i, mid2 = i;//奇数
                while (mid1 >= 0 && mid2 < len && s[mid1] == s[mid2]) {
                    --mid1; ++mid2; ++res;
                }
                
                mid1 = i, mid2 = i+1;//偶数
                while (mid1 >= 0 && mid2 < len && s[mid1] == s[mid2]) {
                    --mid1; ++mid2; ++res;
                }
            }
            return res;
        }
    };
    

    方法三:“马拉车”算法

    神奇的算法,先马一下,学会再写上。听说时间复杂度是 O(n)。

    好了,学到了,请参考:什么是马拉车算法?

    利用马拉车算法,可以得到所有情况下的最大半径,以s[i]为中心,RL[i]为半径的回文串中含有的字回文串数目是 RL[i]/2 个。

    方法三参考代码:

    class Solution {
    public:
        int countSubstrings(string s) {
            //预处理
            string t = "#";
            for (int i = 0; i < s.size(); ++i) {
                t += s[i];
                t += "#";
            }
    
            vector<int> RL(t.size(), 0);
            int MaxRight = 0, pos = 0;
            int res = 0;
            for (int i = 0; i < t.size(); ++i) {
                RL[i] = MaxRight > i ? min(RL[2 * pos - i], MaxRight - i) : 1;
    
                while (i-RL[i] >=0 && i+RL[i] < t.size() && t[i + RL[i]] == t[i - RL[i]])//扩展,注意边界
                    ++RL[i];
                //更新最右端及其中心
                if (MaxRight < i + RL[i] -1) {
                    MaxRight = i + RL[i] -1;
                    pos = i;
                }
                
                res += RL[i]/2;
            }
            return res;
        }
    };
    
  • 相关阅读:
    centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件、目录属性 shell数组简单用法 $( ) 和${ } 和$(( )) 与 sh -n sh -x sh -v 第三十五节课
    基于HTML5 WebGL实现 json工控风机叶轮旋转
    基于HTML5的WebGL实现的2D3D迷宫小游戏
    基于HTML5和WebGL的碰撞测试
    基于HTML5和WebGL的3D网络拓扑结构图
    基于 HTML5 WebGL 的 3D 网络拓扑图
    基于HTML5 Canvas 实现弹出框
    基于HTML5 Canvas实现用户交互
    基于HTML5快速搭建TP-LINK电信拓扑设备面板
    HTML5 技术在风电、光伏等新能源领域的应用
  • 原文地址:https://www.cnblogs.com/qq874455953/p/9901041.html
Copyright © 2020-2023  润新知