leetcode-647-回文子串

文章讨论了如何统计字符串中回文子串的数量,提出了暴力解法、动态规划和双指针三种策略。暴力解法通过逐个尝试所有子串来判断是否为回文,动态规划则利用二维数组记录子串状态,双指针方法从每个字符出发扩展回文串。每种方法都有其适用性和效率考虑。
摘要由CSDN通过智能技术生成

题意描述:

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

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

示例:

输入:s = “abc”
输出:3
解释:三个回文子串: “a”, “b”, “c”

输入:s = “aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”


提示:

1 <= s.length <= 1000
s 由小写英文字母组成

解题思路:
Alice: 看 s 的长度有点小啊,最多也就是 1000 * 999 / 2 种,一个一个去试,估计也不会超时的。
Bob: 嘿,暴力解果然没超时。
Alice: 这题是动态规划吗 ?老是认不出来到底是什么题目。
Bob: 看起来像,回文字符串这种东西不都是前后之间有关系的吗 ?
Alice: 假设有个二维数组 dp,dp[i][j] 表示这个子字符串是不是回文串,递推关系是啥呢 ?
Bob: dp[i][j] = dp[ i + 1][j - 1] && s[i-1] === s[j+1],然后 i-1, j+1 越界的情况要单独求解。
Alice: 具体的求解方式呢 ?
Bob: 先把二维数组的第一行求出来,然后求解数组的右上部分就行了。
Alice: 等一下,你的递推公式里面,为啥是 i + 1j - 1 ?
Bob: 因为 dp[i][j] 表示的是子字符串 s.slice(i, j+1) 是不是回文,i 一定是要小于 j 的,而根据回文的规则,应该是从两端向中间靠拢,也就是 i+1j-1 了。
Alice: 那你的递推规则能覆盖 abba 这种情况吗 ?
Bob: 可以的,这种情况 dp[1][2] = dp[2][1] ... 就属于特殊情况,要单独求解的
Alice: 好像有问题啊,"sfffsfafsffsf" 这个 case 没过
Bob: 是不是二维数组的计算方式不对,这里的计算方向要注意一下,计算 dp[i][j] 要先求出 dp[i+1] 的,也就是应该按照从下到上的顺序求解二维数组的右上部分。
Alice: 原来如此,还有一个问题什么是 i-1, j+1 越界的情况呢 ?
Bob: 也就那几个条件,最小,最大, 左边和右边的大小关系
Alice: 通过了!!


Bob: 我看到好像还有一种双指针的解法,好像更快 ?
Alice: 双指针 ?双指针不行吧 ?怎么处理奇数个的回文串和偶数个的回文串呢 ?双指针从哪里开始,到哪里结束呢 ?
Bob: 如果双指针可以的话,肯定是两个指针,一个往左一个往右,让回文串越来越长,这样才能利用回文的特点。
Alice: 那就是从每个字符开始,向两边扩张?那偶数个的回文串呢 ?一个中心位置能确定一组回文串吗 ?
Bob: 我明白了,双指针扩张的过程中应该总是左右同时增加两个字符,这样的话。
Alice: 这样的话,奇数个的和偶数个的回文串是一样的,只是初始化的时候奇数个的两个指针位置一样,偶数个的两个指针位置是 ii + 1
Bob: 对哦,假设有一个根据双指针的起始位置,计算向两边扩张的过程中的回文字符串。只需要在遍历字符串的时候调用两次这个函数就行了。
Alice: 是的,func(i, i)func(i, i+1)
Bob: 牛啊牛啊


代码:

typescript 暴力

function countSubstrings(s: string): number {

    const chars = s.split('');
    
    const isHuiwen = (startIdx: number, endIdx: number) => {
        while(endIdx >= startIdx && chars[startIdx] === chars[endIdx]) {
            endIdx--;
            startIdx++;
        }
        return endIdx < startIdx;
    }

    let result = chars.length;
    for(let len=2; len<=chars.length; len += 1) {
        for(let i=0; i<chars.length; i++){
            const j = i + len - 1;
            if (j > chars.length) {
                break;
            }
            result  += isHuiwen(i, j) ? 1 : 0;
        }
    }

    return result;
};

动态规划 typescript

function countSubstrings(s: string): number {


    const charList = s.split('');
    const total = charList.length;

    const isHuiwen =  (startIdx: number, endIdx: number) => {
        while(endIdx >= startIdx && charList[startIdx] === charList[endIdx]) {
            endIdx--;
            startIdx++;
        }

        return startIdx > endIdx;
    }

    const isLegalStartEnd = (start: number, end: number) => {
        return start <= end && start >= 0 && end < total;
    }

    const dp = new Array(total);
    for(let i=0; i<total; ++i) {
        dp[i] = new Array(total).fill(false);
    }
    
    let result = 1;
    dp[total-1][total-1] = true;
    
    for(let start=total-2; start>=0; start-= 1){
        for(let end=start; end<total; end+= 1){
            const ss = start + 1;
            const ee = end - 1; 
            dp[start][end] =  isLegalStartEnd(ss, ee) ? dp[ss][ee] && charList[start] === charList[end] : isHuiwen(start, end);
            result += dp[start][end] ? 1 : 0;
        }
    }

    return result;
};

typescript 双指针

function countSubstrings(s: string): number {

    const charList = s.split('');
    const total = charList.length;

    const getPartialResult = (left: number, right: number) => {
        let partialResult = 0;
        while(left >=0 && right < total && charList[left] === charList[right]){
            partialResult += 1;
            left--;
            right++;
        }
        return partialResult;
    }

    let result = 0;
    for(let i=0; i<total; i++){
        result += getPartialResult(i, i);
        result += getPartialResult(i, i+ 1);
    }

    return result;
};

提示

  1. js 中初始化二维数组的方法,不要使用 new Array(len).fill( new Array(len).fill(0) )
    这种方式,这种方式 fill 进去的 Array 是同一个数组,并不是一个真实的二维数组。

参考:

箴言

《黑客帝国》(The Matrix):
“真正重要的东西,是无法被眼睛看到的。” - 摩洛菲斯(Morpheus)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值