问题

求一个字符串有多少个回文子串

Input: "abc"

Output: 3

Input: "aaa"

Output: 6

思路和代码(1)——朴素做法

用dp的基本思想“子问题求解”,但是因为没有重叠子问题,没必要用dp数组或者矩阵来存储中间结果。

直接遍历每个字符,然后以该字符为中心向两边扩张来判断是否回文串,是的话就总数加1。

这时要考虑两种情况,一种是以单字符为中心来扩张即aba这种,另一种是以双字符为中心来扩张即abba。

对于每个字符s[i]要遍历两次扩张,一次以s[i]为中心来扩张,一次以s[i]和s[i+1]为中心来扩张。因此需要遍历2n次(n为字符串长度)。又考虑到最后一个字符s[n-1]后面已经没有字符了,不需要考虑以两个字符为中心的情况,所以实际是遍历2n-1次。

可以用两个指针left和right,单字符为中心的扩张时left和right都初始指向s[i],双字符为中心的扩张时left指向s[i],right指向s[i+1]。

时间复杂度O(n^2),空间复杂度O(1)

class Solution(object):
def countSubstrings(self, s):
"""
:type s: str
:rtype: int
"""
n = len(s)
res = 0
for i in range(2*n-1):
left = i/2
right = left + i%2
while(left >= 0 and right < n and s[left]==s[right] ):
res += 1
left -= 1
right += 1
return res

思路和代码(2)——Manacher算法

使用马拉车算法,算法根据回文串的对称特点利用之前的信息计算,可以在线性时间内找出所有回文串。

“原字符串”转为“#字符串”

前面说了回文串分为单字符中心和双字符中心,为了可以统一按单字符中心处理。首先在原字符中插入字符'#',例如aaa经过插入处理后得到"#a#a#a#",这样遍历到第一个a时其实是以a为单字符中心,遍历到第二个#时就是以aa为双字符中心。第一个#和最后一个#的意义在于补全第一个字符和最后一个字符的“回文串身份”,因为单个字符也算回文串。

镜像计算

我们用p[i]表示以i为中心的回文串半径,p[i]初始化为0。正常情况,我们根据扩张的方法来判断半径是否增加。

特殊情况,如果字符s[i]处于某个已知的“以id为中心,以mx为右边界”的回文串中(此时mx > i),我们称这个回文串为id回文串,此时可以利用id为中心对称过去的镜像索引来简化计算,我们称镜像索引为j。然后有两种情况。

如果p[j]<mx-i,说明i的半径没有超出mx的边界而且等于j的半径,此时p[i]=[j]=p[2*id-1],这个p[i]值是确定的。

如果p[j]>=mx-i,说明i的半径至少扩张到了mx,mx之后的部分要继续扩张。此时p[i]值不是确定的,但是我们让p[i]=mx-i,因为半径为mx-i是至少的,至于半径是否继续增加取决于mx后面的部分,我们在扩张步骤计算。

简化以上两种情况可以得到:如果mx > i,那么p[i] = min(p[2*id-1], mx-i)

扩张

根据上面的说明,只有在mx >i 以及 p[j] < mx-i的情况下可以通过镜像计算来直接获得半径,其它情况还是要继续扩张。扩张的计算很简单,在当前半径的基础上扩张,判断s[i+p[i]+1]和s[i-p[i]-1]是否相等,相等则半径p[i]加1。

更新id回文串

遍历过程中,如果发现有右边界更大的回文串,则覆盖这个回文串。

“#字符串”的回文个数->“原字符串”的回文个数

上面计算的半径p[i]会因为#的插入而增多,所以要根据“#字符串”的半径计算一下,得到“原字符串”的半径。我们把所有半径加起来就可以计算出回文字串的数量。

举个双字符为中心的例子,#a#a#,中间#的半径为a#,要转为半径a,2要转为1,直接除以2就得到了。

举个单字符为中心的例子,#a#a#b#a#a#,b的半径#a#a#,要转为半径aa,5要转为2,但是考虑b本身也是一个回文串,实际上是5要转为3,这个时候需要加1之后除以2(这里跟半径的定义有关,我这里的定义中b这个单字符的半径为0。如果定义的b的半径为1,这里的计算就不是加1而是减1了)。

综合来看,直接加1除以2就可以了,因为/会向下取整。

时间复杂度O(n),空间复杂度O(1)

class Solution(object):
def countSubstrings(self, s):
"""
:type s: str
:rtype: int
"""
s = '#' + '#'.join(s) + '#'
n = len(s)
p = [0]*n
id = mx = res = 0
for i in range(1,n-1):
if(mx > i):
p[i] = min(mx-i, p[2*id-i])
while i+p[i]+1 < n and i-p[i]-1 >=0 and s[i+p[i]+1] == s[i-p[i]-1]:
p[i] += 1
if(i + p[i] > mx):
id = i
mx = i + p[i]
res += (p[i]+1)/2
return res

647. Palindromic Substrings(马拉车算法)的更多相关文章

  1. 【LeetCode】647. Palindromic Substrings 解题报告(Python)

    [LeetCode]647. Palindromic Substrings 解题报告(Python) 标签: LeetCode 题目地址:https://leetcode.com/problems/p ...

  2. Leetcode 647. Palindromic Substrings

    Given a string, your task is to count how many palindromic substrings in this string. The substrings ...

  3. 2015 UESTC 搜索专题M题 Palindromic String 马拉车算法

    Palindromic String Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/s ...

  4. 647. Palindromic Substrings 互文的子字符串

    [抄题]: Given a string, your task is to count how many palindromic substrings in this string. The subs ...

  5. [LeetCode] 647. Palindromic Substrings 回文子字符串

    Given a string, your task is to count how many palindromic substrings in this string. The substrings ...

  6. 647. Palindromic Substrings

    Given a string, your task is to count how many palindromic substrings in this string. The substrings ...

  7. 【Leetcode】647. Palindromic Substrings

    Description Given a string, your task is to count how many palindromic substrings in this string. Th ...

  8. LeetCode 647. Palindromic Substrings的三种解法

    转载地址 https://www.cnblogs.com/AlvinZH/p/8527668.html#_label5 题目详情 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串. 具有不同 ...

  9. 【LeetCode】647. Palindromic Substrings 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:暴力循环 方法二:固定起点向后找 方法三:动 ...

随机推荐

  1. ETL概念,ETL流程

    ETL是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程,目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提供分析依据. ETL是BI项目重要的一个环节. 通常情况下,在 ...

  2. JQuery------鼠标双击时,不选中div里面的文字

    如图:(去掉选中文字的蓝色背景色) 代码: //方法一:<div class="test" onselectstart="return false" &g ...

  3. Spring学习笔记 7.1 Spring MVC起步

    7.1.1 跟踪Spring MVC的请求请求首先到达DispatcherServlet(DispatcherServlet是Spring MVC中的前端控制器):DispatcherServlet的 ...

  4. 使用JSP表达式和JSP脚本打印九九乘法表

    首先使用JSP声明声明一个函数用于得到九九乘法表的内容 <%! String printMultiTable() { String s = ""; for (int i = ...

  5. oracle怎么把一个用户下的表复制给另一个用户?(授予表权限)

    //把system读写权限 授权给scottselect 'Grant all on '||table_name||' to scott;' from all_tables where owner = ...

  6. vector排序问题<unresolved overloaded function type>

    要对vector中的自定义类型进行排序,首先需要提供一个函数bool comp(const Interval & a, const Interval & b) 来定义类型的排序准则 然 ...

  7. poj3585 Accumulation Degree【树形DP】【最大流】

    Accumulation Degree Time Limit: 5000MS   Memory Limit: 65536K Total Submissions:3151   Accepted: 783 ...

  8. 修改记事本默认编码为UTF-8

    1. 新建一个txt文档,不输入任何内容.然后“另存为”,将编码由默认的 ANSI 修改为 Unicode 或 UTF-8,并将新文档命名为 temp.txt 2.将 temp.txt 移动至系统目录 ...

  9. Mac自带Apache和Php

    Mac 是默认安装 apache和php,但是需要使用root用户来启用,所以请按照我下面的步骤来: 一.启用root用户1.选取系统偏好设置....2.从显示菜单中,选取“帐户”.3.点按锁图标并使 ...

  10. 实现一个自动生成小学四则运算题目的命令行程序(java实现)

    Github项目地址:https://github.com/xiaobaot/wordcount/tree/master/sizeyusuan 团队成员:谢家明(代码生成)    谢竣(测试完善) 项 ...