首页 > 其他分享 >[LeetCode] 1316. Distinct Echo Substrings 不同的循环子字符串

[LeetCode] 1316. Distinct Echo Substrings 不同的循环子字符串

时间:2022-10-02 05:11:05浏览次数:91  
标签:1316 hash Distinct text len Echo 回声 哈希 字符串


Return the number of distinct non-empty substrings of text that can be written as the concatenation of some string with itself (i.e. it can be written as a + a where a is some string).

Example 1:

Input: text = "abcabcabc"
Output: 3
Explanation: The 3 substrings are "abcabc", "bcabca" and "cabcab".

Example 2:

Input: text = "leetcodeleetcode"
Output: 2
Explanation: The 2 substrings are "ee" and "leetcodeleetcode".

Constraints:

  • 1 <= text.length <= 2000
  • text has only lowercase English letters.

这道题定义了一种回声字符串 Echo String,就是说可以拆分成两个相同的子串的字符串,比如 abcabc,可以拆分成两个 abc,这就是一个回声串。现在给定了一个字符串,问其有多少个不同的回声子串。这道题的难点就在于如何查找回声串,我们之前应该做过很多回文串的题目,跟这里的回声串很像,只不过重复的字母顺序不同,还是需要逐个字母的来比较。这道题是要求不同的回声字符串的个数,为了避免重复,可以将找到的回声串放到一个 HashSet 中,利用其自动去重复的特点。

根据回声串的定义可以得知其最小长度应该为2,其重复的字符长度最小为1,最大为 n/2,可以按长度来遍历所有可能的回声串。len 是重复字符串的长度,从1遍历到 n/2,这里还是要用双指针来做,i是第一个重复字符串的起始位置,j是第二个重复字符串的起始位置,初始化为 len,还需要一个变量 cnt 来记录当前已匹配的字符个数。在循环中判断,若 text[i] 等于 text[j],则 cnt 自增1,否则 cnt 重置为0,若此时 cnt 等于 len 了,则将整个回声串取出加到 HashSet 中,cnt 自减1。这里为何要自减1呢,因为两个指针同时右移一位后,若字母相等,则 cnt 还会自增1,若之前不减1,则无法触发加入 HashSet 操作。循环结束后返回 HashSet 的长度即可,参见代码如下:


解法一:

class Solution {
public:
    int distinctEchoSubstrings(string text) {
        unordered_set<string> st;
        int n = text.size();
        for (int len = 1; len <= n / 2; ++len) {
            for (int i = 0, j = len, cnt = 0; i < n - len; ++i, ++j) {
                if (text[i] == text[j]) ++cnt;
                else cnt = 0;
                if (cnt == len) {
                    st.insert(text.substr(i, len));
                    --cnt;
                }
            }
        }
        return st.size();
    }
};

再来看一种滚动哈希 Rolling Hash 的解法,其经常被用于快速匹配字符串的 Rabin-Karp 算法,这种算法实际上是把每个子串都编码成一个独特的长整型数,根据这个编码值就可以快速的判断子串是否相等。具体来说,这里取了两个互质的数字 BASE 和 M,对于字符串 abcd,定义哈希函数为:

(a * BASE^3 + b * BASE^2 + c * BASE^1 + d * BASE^0) % M

这里的字符是用其 ASCII 码来表示的, BASE^k 是固定量,可以放在一个数组 power 中,同时可以计算出 [0, 1], [0, 2], [0, 3], ... [0, n] 范围内的所有哈希值,保存在 hash 数组中,则此时 hash[i] 就表示前i个字符组成的字符串的哈希值(注意这里为了计算方便,字符串的高位是在最右边)。这实际上就类似于累加和数组,可以快速求出任意区间的哈希值。

此时按照长度来遍历所有区间,len 的范围是 [2, n-i],i为此时的左边界位置,取其中点位置 i + len/2,现在的目标是判断区间 [i, mid] 和 [mid, i+len] 之间的子串是否相等。可以通过计算其哈希值来判断,计算哈希值的方法就类似于累加和数组求任意区间之和,计算区间 [i, j] 的哈希值的方法为 (hash[j] - hash[i] * power[j - i] % M + M) % M,这里不停的对M取余就是为了防止溢出,参见代码如下:


解法二:

class Solution {
public:
    int distinctEchoSubstrings(string text) {
        unordered_set<long> st;
        int n = text.size(), BASE = 29, M = 1e9 + 7;
        vector<long> hash(n + 1), power(n + 1);
        power[0] = 1;
        for (int i = 1; i <= n; ++i) {
            hash[i] = (hash[i - 1] * BASE + text[i - 1]) % M;
            power[i] = power[i - 1] * BASE % M;
        }
        for (int i = 0; i < n; ++i) {
            for (int len = 2; i + len <= n; len += 2) {
                int mid = i + len / 2;
                long hash1 = getHash(i, mid, hash, power, M);
                long hash2 = getHash(mid, i + len, hash, power, M);
                if (hash1 == hash2) st.insert(hash1);
            }
        }
        return st.size();
    }
    long getHash(int i, int j, vector<long>& hash, vector<long>& power, int M) {
        return (hash[j] - hash[i] * power[j - i] % M + M) % M;
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/1316


类似题目:

Find Substring With Given Hash Value


参考资料:

https://leetcode.com/problems/distinct-echo-substrings/

https://leetcode.com/problems/distinct-echo-substrings/discuss/492704/Intuitive100-Sliding-Counter-(with-pictures)

https://leetcode.com/problems/distinct-echo-substrings/discuss/477217/Java-Brute-force-and-Hash-Solution-Clean-code


LeetCode All in One 题目讲解汇总(持续更新中...)

标签:1316,hash,Distinct,text,len,Echo,回声,哈希,字符串
From: https://www.cnblogs.com/grandyang/p/16748197.html

相关文章

  • 删除DataTable重复列,类似数据库的Distinct函数。
    将数据表放到内存中进行操作,可以极大的提高效率。///<summary>///删除DataTable重复列,类似distinct///</summary>///<paramna......
  • 命令:@echo off
    @echooff关闭命令回显,表示执行了这条命令后关闭所有命令(包括本身这条命令)的回显。echooff执行以后,后面所有的命令均不显示,但本条命令是显示的。@的作用就是关......
  • stream流之distinct
    1、对于string的去重直接使用distinct()publicvoidtest(){List<String>strList=newArrayList<>();strList.add("A");strList.add("A");strLis......
  • 题解【CF1316E Team Building】网络流做法
    题目传送门。一眼费用流。然后发现题解区竟然全是状压DP?????推销一下本题状压DP的题解。那么我就来yy一下我的网络流做法吧,我会尽量把网络流的想法讲得自然一点。考......
  • 题解【CF1316E Team Building】
    题目传送门状压DP入门题。设\(f_{i,S}\)表示考虑了前\(i\)个人,队伍放置情况为\(S\)时(0表示放置了队员,1表示没有放置)的最大贡献。然后分讨一下\(i\)是去当队......
  • bash 中 echo & printf
    首先列一下今天收获的消息sh是:BourneShell(/usr/bin/sh或/bin/sh)bash是:BourneAgainShell(/bin/bash)printf后面跟的两个连续字符串参数先结合,最后一起被printf执......
  • Warning message "Partial Early Aggregation/Distinct running with reduced memory"
    Warningmessage"PartialEarlyAggregation/Distinctrunningwithreducedmemory"https://www.ibm.com/support/pages/warning-message-partial-early-a......
  • [ABC236H] Distinct Multiples
    题目传送门Solution首先不难想到容斥,我们可以钦定若干关系\((u,v)\),表示\(u,v\)的值相同,那么我们不妨设\(g(i)\)表示至少有\(i\)种这种关系的方案数,可以发现答案......
  • linux查看日志文件内容命令tail、cat、tac、head、echo
    linux查看日志文件内容命令tail、cat、tac、head、echo-大自然的流风-博客园 https://www.cnblogs.com/zdz8207/p/linux-log-tail-cat-tac.htmllinux查看日志文件内......
  • Linq:Distinct()不能排除重复对象的解决方案
    1.数据准备:假设有几个重复数据,如下,(正常使用Distinct()方法,我们想要排除掉重复的对象)usingSystem.Collections.Generic;namespaceLINQTutorial{publicclassS......