leetcode-环绕字符串中唯一的子字符串

273 阅读3分钟

「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」。

题目

把字符串 s 看作是 “abcdefghijklmnopqrstuvwxyz” 的无限环绕字符串,所以 s 看起来是这样的: "...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd...." . 
现在给定另一个字符串 p 。返回 s 中 唯一 的 p 的 非空子串 的数量 。 

示例 1:
输入:p = "a"
输出:1
解释:字符串 s 中只有一个"a"子字符。

示例 2:
输入:p = "cac"
输出:2
解释:字符串 s 中的字符串“cac”只有两个子串“a”、“c”。.

示例 3:
输入:p = "zab"
输出:6
解释:在字符串 s 中有六个子串“z”、“a”、“b”、“za”、“ab”、“zab”。

思路

动态规划的题目,不过这题比起一般的dp,更需要一些经验。
首先理解题意,要求p是s的字串,s是一个无限环绕的,连续的,所以p也必须按照字符顺序连续,即类似m后面是n,j后面是k这样的,跟字符顺序唯一的不同就是z后面是a。
理解了题意之后,我们想怎么求出唯一的p的数量。

方案一:

如果我们找到了连续的字串,假设是abc,长度为3,那么我们发现在abc这个字串里面,

  • 长度为1的子串有3个:a、b、c
  • 长度为2的子串有2个:ab、bc
  • 长度为3的子串有1个:abc 所以,如果找到长度为3的字串,那么实际对应的字串数量是1+2+3=3(3+1)/2=6;推广到长度为n的字串,实际对应的字串数量为1+2+...+n=n(n+1)/2。 这样,问题就演化成了,在p中寻找这样连续字串,找到后用高斯求和求出是对应字串的数量,再求和即可。题目还有一个要求是唯一,所以,这里还要去重,可以使用哈希表做,不过空间和时间代价很大,每个长度为n的字串,都要对应处理n(n+1)/2次,也需要对应n(n+1)/2的空间。
方案二:

我们定义一个一维int数组dp,dp[index]代表以'a'+index作为结尾的连续字串最大长度。
这个方法最重要的一个点:以字符char为结尾的连续字串最大长度就是以字符char为结果的不重复字串数量 例如以b结尾的连续字串最大长度是3,那么对应的3个不重复字串就是b、ab、zab。因为dp每个元素代表不同的结尾,所以dp之间不会重复;因为1个dp中len个字串长度不同,所以不会重复;因为字串的结尾可能就是a-z,所以dp没有遗漏;综合起来,对dp求和就是s 中唯一的p的非空子串的数量。
剩下的问题就是怎么求解dp,我们可以遍历p,找到每段连续的字串p~,然后求出p~的长度len,结尾的字串是char,那么状态转移方程就是

dp[chan-'a'] = max(dp[char-'a'],len)

Java版本代码

class Solution {
    public int findSubstringInWraproundString(String p) {
        // dp用于记录以'a'+index作为结尾的连续字串最大长度
        int dp[] = new int[26];
        int len = 0;
        // 随便给一个初始值,下面循环中会更新
        char pre = 'z';
        for (int i = 0; i < p.length(); i++) {
            char current = p.charAt(i);
            if (i > 0 && isContinue(pre, current)) {
                len++;
            } else {
                len = 1;
            }
            dp[current-'a'] = Integer.max(dp[current-'a'], len);
            pre = current;
        }
        int ans = Arrays.stream(dp).sum();
        return ans;
    }

    private static boolean isContinue(char a, char b) {
        if (b - a == 1) {
            return true;
        }
        if (a == 'z' && b == 'a') {
            return true;
        }
        return false;
    }
}