【问题标题】:Permutation of string as substring of another字符串的排列作为另一个的子字符串
【发布时间】:2014-05-09 08:46:24
【问题描述】:

给定一个字符串 A 和另一个字符串 B。找出 B 的任何排列是否作为 A 的子字符串存在。

例如,

如果 A = “百科全书”

如果 B="dep" 则返回 true,因为 ped 是 dep 的排列,ped 是 A 的子字符串。

My solution->

if length(A)=n and length(B)=m

I did this in 0((n-m+1)*m) by sorting B and then checking A 
with window size of m each time.

我需要找到更好更快的解决方案。

【问题讨论】:

  • 这已经是一个好方法了。稍微快一点的方法是简单地计算 B 中每个字符的频率,然后查看这些计数是否与您考虑的 A 的每个窗口中的计数匹配。
  • 您可以使用这种方法在每个窗口的 O(1) 时间内轻松更新 B 的频率向量 - 只需为传出字符减去 1 的计数,为传入的字符添加 1 的计数。
  • 能不能详细解释一下
  • 1.在freqB[i] 中建立字符 i 在 B 中出现的次数。(例如,在您的示例中,freqB['d'] == freqB['e'] == freqB['p'] == 1freqB[i] == 0 用于所有其他字符 i。) 2. 对于 A 的每个长度-m 窗口,做同样的事情,但将它们存储在freqA[] 中,然后检查每个字符i 是否有freqA[i] == freqB[i]。如果是这样,你有一个匹配。要从位置 j 开始的 length-m 窗口移动到下一个窗口,您需要执行 --freqA[A[j]]++freqA[A[j+m]]
  • 美丽的解决方案...谢谢!!

标签: string algorithm substring permutation


【解决方案1】:

这个问题有一个更简单的解决方案,可以在线性时间内完成。

这里:n = A.size (), m = B.size ()

这个想法是使用散列

首先我们对字符串B的字符进行哈希处理。

假设:B = "dep"

  • hash_B ['d'] = 1;
  • hash_B ['e'] = 1;
  • hash_B ['p'] = 1;

现在我们为每个大小为“m”的窗口在字符串“A”上运行一个循环。

假设:A = "百科全书"

大小为 'm' 的第一个窗口将包含字符 {e, n, c}。我们现在将对它们进行哈希处理。

  • win ['e'] = 1
  • 赢 ['n'] = 1
  • win ['c'] = 1

现在我们检查两个数组(hash_B []win [])中每个字符的频率是否相同。注意:hash_B [] 或 win [] 的最大大小为 26。

如果它们不相同,我们会移动窗口。

在移动窗口后,我们减少 win ['e'] 的次数减少 1增加 win 的次数['y'] 1

  • 赢 ['n'] = 1
  • win ['c'] = 1
  • 赢 ['y'] = 1

第七班期间,你的win数组状态为:

  • 赢 ['p'] = 1;
  • win ['e'] = 1;
  • win ['d'] = 1;

与 hash_B 数组相同。所以,打印 "SUCCESS" 并退出。

【讨论】:

  • 谢谢。这对我来说是最明确的答案。它与上面描述的窗口方法相同,但有更好的示例。一个问题:为什么在 O 计算中是 26?
  • @KatieSissons 感谢您指出这一点。编辑了答案:)
  • 为什么称它为线性时间?这不是线性时间,这里的时间是O(|B| * |A|),还有一个解是O(|A| + |B|)。
  • @them,是的,如果 B 是 A 的子串意味着更小的字符串,0(|A| + |B|) 被简化为 0(|A|),只是为了好玩
【解决方案2】:

如果我只需要担心 ASCII 字符,可以在 O(n) 时间和 O(1) 空格内完成。我的代码也打印出排列,但可以很容易地修改为在第一个实例中简单地返回 true。代码的主要部分位于printAllPermutations() 方法中。这是我的解决方案:

一些背景

这是我想出的一个解决方案,它有点类似于 Rabin Karp 算法背后的想法。在我理解算法之前,我将解释它背后的数学如下:

S = {A_1, ..., A_n} 是一个大小为 N 且仅包含素数的 multiset 列表。让 S 中的数字之和等于某个整数 Q。那么 S 是唯一可能的大小为 N 的完全素数多重集,其元素之和可以为 Q

因此,我们知道我们可以将每个字符映射到一个素数。我建议的地图如下:

1 -> 1st prime
2 -> 2nd prime
3 -> 3rd prime
...
n -> nth prime

如果我们这样做(我们可以这样做,因为 ASCII 只有 256 个可能的字符),那么我们很容易找到较大字符串 B 中的每个排列。

算法:

我们将做以下事情:

1:计算A中每个字符映射到的素数之和,我们称之为smallHash。

2:创建 2 个索引(righti 和 lefti)。 righti 被初始化为零,lefti 被初始化为 A 的大小。

ex:     |  |
        v  v
       "abcdabcd"
        ^  ^
        |  |

3:创建一个变量currHash,并将其初始化为B中每个字符映射到的相应素数之和,介于(包括)righti和lefti - 1之间。

4:将 righti 和 lefti 都迭代 1,每次更新 currHash,方法是从不再在范围内的字符 (lefti - 1) 中减去映射的素数,并添加与刚刚添加到范围内的字符对应的素数(对)

5:每次currHash等于smallHash时,范围内的字符必须是一个排列。所以我们把它们打印出来。

6:继续直到我们到达B的末尾。(当righti等于B的长度时)

此解决方案在O(n) 时间复杂度和O(1) 空间中运行。

实际代码:

public class FindPermutationsInString {
    //This is an array containing the first 256 prime numbers
    static int primes[] = 
          {
            2,     3,     5,     7,    11,    13,    17,    19,    23,    29,
            31,    37,    41,    43,    47,    53,    59,    61,    67,    71,
            73,    79,    83,    89,    97,   101,   103,   107,   109,   113,
            127,   131,   137,   139,   149,   151,   157,   163,   167,   173,
            179,   181,   191,   193,   197,   199,   211,   223,   227,   229,
            233,   239,   241,   251,   257,   263,   269,   271,   277,   281,
            283,   293,   307,   311,   313,   317,   331,   337,   347,   349,
            353,   359,   367,   373,   379,   383,   389,   397,   401,   409,
            419,   421,   431,   433,   439,   443,   449,   457,   461,   463,
            467,   479,   487,   491,   499,   503,   509,   521,   523,   541,
            547,   557,   563,   569,   571,   577,   587,   593,   599,   601,
            607,   613,   617,   619,   631,   641,   643,   647,   653,   659,
            661,   673,   677,   683,   691,   701,   709,   719,   727,   733,
            739,   743,   751,   757,   761,   769,   773,   787,   797,   809,
            811,   821,   823,   827,   829,   839,   853,   857,   859,   863,
            877,   881,   883,   887,   907,   911,   919,   929,   937,   941,
            947,   953,   967,   971,   977,   983,   991,   997,  1009,  1013,
           1019,  1021,  1031,  1033,  1039,  1049,  1051,  1061,  1063,  1069,
           1087,  1091,  1093,  1097,  1103,  1109,  1117,  1123,  1129,  1151,
           1153,  1163,  1171,  1181,  1187,  1193,  1201,  1213,  1217,  1223,
           1229,  1231,  1237,  1249,  1259,  1277,  1279,  1283,  1289,  1291,
           1297,  1301,  1303,  1307,  1319,  1321,  1327,  1361,  1367,  1373,
           1381,  1399,  1409,  1423,  1427,  1429,  1433,  1439,  1447,  1451,
           1453,  1459,  1471,  1481,  1483,  1487,  1489,  1493,  1499,  1511,
           1523,  1531,  1543,  1549,  1553,  1559,  1567,  1571,  1579,  1583,
           1597,  1601,  1607,  1609,  1613,  1619
          };

    public static void main(String[] args) {
        String big = "abcdabcd";
        String small = "abcd";
        printAllPermutations(big, small);
    }

    static void printAllPermutations(String big, String small) {

        // If the big one is smaller than the small one,
        // there can't be any permutations, so return
        if (big.length() < small.length()) return;

        // Initialize smallHash to be the sum of the primes
        // corresponding to each of the characters in small.
        int smallHash = primeHash(small, 0, small.length());

        // Initialize righti and lefti.
        int lefti = 0, righti = small.length();

        // Initialize smallHash to be the sum of the primes
        // corresponding to each of the characters in big.
        int currentHash = primeHash(small, 0, righti);

        while (righti <= big.length()) {
            // If the current section of big is a permutation
            // of small, print it out.
            if (currentHash == smallHash)
                System.out.println(big.substring(lefti, righti));

            // Subtract the corresponding prime value in position
            // lefti. Then increment lefti
            currentHash -= primeHash(big.charAt(lefti++));

            if (righti < big.length()) // To prevent index out of bounds
                // Add the corresponding prime value in position righti.
                currentHash += primeHash(big.charAt(righti));

            //Increment righti.
            righti++;
        }

    }

    // Gets the sum of all the nth primes corresponding
    // to n being each of the characters in str, starting
    // from position start, and ending at position end - 1.
    static int primeHash(String str, int start, int end) {
        int value = 0;
        for (int i = start; i < end; i++) {
            value += primeHash(str.charAt(i));
        }
        return value;
    }

    // Get's the n-th prime, where n is the ASCII value of chr
    static int primeHash(Character chr) {
        return primes[chr];
    }
}

但请记住,此解决方案仅适用于字符只能是 ASCII 字符的情况。如果我们谈论的是 unicode,我们开始涉及超过 int 甚至 double 的最大大小的素数。另外,我不确定是否有 1,114,112 个已知素数。

【讨论】:

  • 我喜欢这个答案,除了你说你加/减时间的部分,它是乘法/除法。 2个素数之和不是唯一的,2个素数的乘积是唯一的。
  • 你能告诉我你使用的数学名称是什么吗?我发现 2 个不是彼此排列的单词具有相同的哈希值。请看:pastebin.com/c2TuXENY
  • 这不是O(|A|+|B|),不是O的单个变量吗?计算smallHashO(|B|),遍历AO(|A|)。不过我喜欢这个解决方案,使用素数和它们的产品进行散列是优雅的。
  • 这非常优雅,但不幸的是这里必须同意@TronyTr。显示碰撞的可运行 Python 代码:pastebin.com/BuvWeGVV。我知道 radix-2p 散列可用于确定两个字符串是否是彼此的排列,但不可能像在步骤 4 中那样以链式方式计算 radix-2p 散列(必须提高每个字符的 2p根据它在字符串中的位置,到不同的幂)。
  • 提供了一个固定的C++ 实现in this new answer
【解决方案3】:

在 cmets 中 j_random_hacker 提出的算法的基础上,可以在O(|A|+|B|) 中找到匹配项,如下所示:(注:始终使用|A| 表示“A 的长度” .)

  1. 创建一个整数数组count,其域是字母表的大小,初始化为所有0s。
  2. distance 设置为0
  3. 对于B 中的每个字符B<sub>i</sub>
    • 递减count[B<sub>i</sub>]
    • 如果count[B<sub>i</sub>] 的先前计数是0,则还增加distance
  4. 对于A 中的每个字符A<sub>i</sub>
    • 递增count[A<sub>i</sub>]
    • 如果i 大于|B|,则减少count[A<sub>i-|B|</sub>]
    • 对于修改的两个count 值中的每一个,如果之前的值是0,则增加distance,如果新值是0,则减少distance
    • 如果结果是 distance0 则已找到匹配项。

注意:j_random_hacker提出的算法也是O(|A|+|B]),因为比较freqAfreqB的代价是O(|alphabet|),这是一个常数。然而,上述算法将比较成本降低到一个small常数。此外,通过使用未初始化数组的标准技巧,即使字母表不是恒定大小,理论上也可以使这项工作。

【讨论】:

  • 我喜欢!所以distance 是目前正在考虑的子字符串(来自 A 和来自 B)中计数不一致的字符数。而且我认为将字母大小作为参数 k 是公平的,因此我的算法是 O(k|A| + |B|) 而你的算法是 O(|A| + |B| + k)。
  • |B|
  • @tzs:“毫无意义”似乎有点言过其实。 “冗余”是一个合理的批评。
【解决方案4】:

这个答案提出了@Ephraim 在他自己的answer 中提出的想法的固定 实现。

最初的答案混淆了给定素数集合的乘法属性和加法。固定的语句是:

S = {A_1, ..., A_n} 是一个大小为 N 且仅包含素数的 multiset 列表。让 S 中的数字的 product 等于某个整数 Q。那么 S 是唯一可能的大小为 N 的完全素数多重集,其元素可以Q。 p>

为了避免数值溢出,实现使用基于C++libgmpxx无限精度算法

假设两个数的比较在O(1),那么解在O(|A| + |B|)。对于|B| 足够大的输入,前面的假设实际上可能并非如此。当|B| &gt; |A| 函数返回O(1)

示例:

#include <iostream>
#include <string>
#include <gmpxx.h>

static int primes[] =
          {
            2,     3,     5,     7,    11,    13,    17,    19,    23,    29,
            31,    37,    41,    43,    47,    53,    59,    61,    67,    71,
            73,    79,    83,    89,    97,   101,   103,   107,   109,   113,
            127,   131,   137,   139,   149,   151,   157,   163,   167,   173,
            179,   181,   191,   193,   197,   199,   211,   223,   227,   229,
            233,   239,   241,   251,   257,   263,   269,   271,   277,   281,
            283,   293,   307,   311,   313,   317,   331,   337,   347,   349,
            353,   359,   367,   373,   379,   383,   389,   397,   401,   409,
            419,   421,   431,   433,   439,   443,   449,   457,   461,   463,
            467,   479,   487,   491,   499,   503,   509,   521,   523,   541,
            547,   557,   563,   569,   571,   577,   587,   593,   599,   601,
            607,   613,   617,   619,   631,   641,   643,   647,   653,   659,
            661,   673,   677,   683,   691,   701,   709,   719,   727,   733,
            739,   743,   751,   757,   761,   769,   773,   787,   797,   809,
            811,   821,   823,   827,   829,   839,   853,   857,   859,   863,
            877,   881,   883,   887,   907,   911,   919,   929,   937,   941,
            947,   953,   967,   971,   977,   983,   991,   997,  1009,  1013,
           1019,  1021,  1031,  1033,  1039,  1049,  1051,  1061,  1063,  1069,
           1087,  1091,  1093,  1097,  1103,  1109,  1117,  1123,  1129,  1151,
           1153,  1163,  1171,  1181,  1187,  1193,  1201,  1213,  1217,  1223,
           1229,  1231,  1237,  1249,  1259,  1277,  1279,  1283,  1289,  1291,
           1297,  1301,  1303,  1307,  1319,  1321,  1327,  1361,  1367,  1373,
           1381,  1399,  1409,  1423,  1427,  1429,  1433,  1439,  1447,  1451,
           1453,  1459,  1471,  1481,  1483,  1487,  1489,  1493,  1499,  1511,
           1523,  1531,  1543,  1549,  1553,  1559,  1567,  1571,  1579,  1583,
           1597,  1601,  1607,  1609,  1613,  1619
          };

mpz_class prime_hash(std::string const &str, size_t start, size_t end)
{
    mpz_class hash(1);
    for (size_t i = start; i < end; ++i) {
        hash *= primes[(size_t) str.at(i)];
    }
    return hash;
}

void print_all_permutations(std::string const &big, std::string const &small)
{
    const size_t big_len = big.length();
    const size_t small_len = small.length();

    if (small_len <= 0 || big_len < small_len) {
        // no possible permutation!
        return;
    }

    // O(small_len)
    mpz_class small_hash = prime_hash(small, 0, small_len);
    mpz_class curr_hash = prime_hash(big, 0, small_len);

    // O(big_len)
    size_t left_idx = 0;
    do {

        if (curr_hash == small_hash) {
            std::cout << "Permutation `" << big.substr(left_idx, small_len)
                      << "' of `" << small
                      << "' at index " << left_idx
                      << " of `" << big
                      << "'." << std::endl;
        }

        curr_hash /= primes[(size_t) big.at(left_idx)];

        if (left_idx + small_len < big_len) {
            curr_hash *= primes[(size_t) big.at(left_idx + small_len)];
        }

        ++left_idx;

    } while (left_idx + small_len < big_len);
}

int main(int argc, char *argv[])
{
    // @user2826957's input
    print_all_permutations("encyclopedia", "dep");

    // @Ephraim's input
    print_all_permutations("abcdabcd", "abcd");

    // @Sam's input
    print_all_permutations("cbabadcbbabbc", "abbc");

    // @butt0s input
    print_all_permutations("", "");
    print_all_permutations("", "a");
    print_all_permutations("a", "");
    print_all_permutations("a", "a");

    return 0;
}

示例编译为:

~$ g++ permstr.cpp -lgmpxx -lgmp -o run
~$ ./run
Permutation `ped' of `dep' at index 7 of `encyclopedia'.
Permutation `abcd' of `abcd' at index 0 of `abcdabcd'.
Permutation `bcda' of `abcd' at index 1 of `abcdabcd'.
Permutation `cdab' of `abcd' at index 2 of `abcdabcd'.
Permutation `dabc' of `abcd' at index 3 of `abcdabcd'.
Permutation `cbab' of `abbc' at index 0 of `cbabadcbbabbc'.
Permutation `cbba' of `abbc' at index 6 of `cbabadcbbabbc'.
Permutation `a' of `a' at index 0 of `a'.

【讨论】:

    【解决方案5】:

    我的做法是先给自己举个大例子,比如

    a: abbc b: cbabadcbbabbc 然后逐字逐句地检查并在每个排列下划线 a: abbc b: cbabadcbbabbc '__' '__' '__' 所以 For i-> b.len: sub = b.substring(i,i+len) isPermuted ? 这是java中的代码

    class Test {
      public static boolean isPermuted(int [] asciiA, String subB){
        int [] asciiB = new int[26];
    
        for(int i=0; i < subB.length();i++){
          asciiB[subB.charAt(i) - 'a']++;
        }
        for(int i=0; i < 26;i++){
            if(asciiA[i] != asciiB[i])
            return false;
        }
        return true;
      }
      public static void main(String args[]){
        String a = "abbc";
        String b = "cbabadcbbabbc";
        int len = a.length();
        int [] asciiA = new int[26];
        for(int i=0;i<a.length();i++){
          asciiA[a.charAt(i) - 'a']++;
        }
        int lastSeenIndex=0;
        for(int i=0;i<b.length()-len+1;i++){
          String sub = b.substring(i,i+len);
          System.out.printf("%s,%s\n",sub,isPermuted(asciiA,sub));
    } }
    }
    

    【讨论】:

    • 这个解决方案的复杂性是什么?
    • 时间复杂度为 O(A + B * (A + A))。第一个A - 填充“asciiA”,第二个A - 制作子字符串“sub”(可以通过发送indexStart,indexEnd进行优化,因此我们不需要复制),第三个A - 填充“asciiB”。并且 0(A + B * (A + A)) 简化为 O(B*A)。 A - a 的长度,B - b 的长度。额外的空间复杂度为 O(1)。
    【解决方案6】:

    如果字符串 B 是字符串 A 的置换子字符串,则以下函数将返回 true。

    public boolean isPermutedSubstring(String B, String A){
        int[] arr = new int[26];
    
        for(int i = 0 ; i < A.length();++i){
            arr[A.charAt(i) - 'a']++;
        }
        for(int j=0; j < B.length();++j){
            if(--arr[B.charAt(j)-'a']<0) return false;
        }
        return true;
    }
    

    【讨论】:

      【解决方案7】:

      上面的讨论很清楚这个想法。下面是一个时间复杂度为 O(n) 的实现。

      #include <stdio.h>
      #include <string.h>
      
      const char *a = "dep";
      const char *b = "encyclopediaped";
      
      int cnt_a[26];
      int cnt_b[26];
      
      int main(void)
      {
          const int len_a = strlen(a);
          const int len_b = strlen(b);
      
          for (int i = 0; i < len_a; i++) {
                  cnt_a[a[i]-'a']++;
                  cnt_b[b[i]-'a']++;
          }
      
          int i;
          for (i = 0; i < len_b-len_a; i++) {
                  if (memcmp(cnt_a, cnt_b, sizeof(cnt_a)) == 0)
                          printf("%d\n", i);
                  cnt_b[b[i]-'a']--;
                  cnt_b[b[i+len_a]-'a']++;
          }
          if (memcmp(cnt_a, cnt_b, sizeof(cnt_a)) == 0)
                  printf("%d\n", i);
      
          return 0;
      }
      

      【讨论】:

        【解决方案8】:

        这是一个几乎是 rici 的答案的解决方案。 https://wandbox.org/permlink/PdzyFvv8yDf3t69l 它为频率表分配了多于 1k 的堆栈内存。 O(|A| + |B|),没有堆分配。

        #include <string>
        
        bool is_permuted_substring(std::string_view input_string,
                                   std::string_view search_string) {
          if (search_string.empty()) {
            return true;
          }
        
          if (search_string.length() > input_string.length()) {
            return false;
          }
        
          int character_frequencies[256]{};
          auto distance = search_string.length();
          for (auto c : search_string) {
            character_frequencies[(uint8_t)c]++;
          }
        
          for (auto i = 0u; i < input_string.length(); ++i) {
            auto& cur_frequency = character_frequencies[(uint8_t)input_string[i]];
            if (cur_frequency > 0) distance--;
            cur_frequency--;
        
            if (i >= search_string.length()) {
              auto& prev_frequency = ++character_frequencies[(
                  uint8_t)input_string[i - search_string.length()]];
              if (prev_frequency > 0) {
                distance++;
              }
            }
        
            if (!distance) return true;
          }
        
          return false;
        }
        
        int main() {
          auto test = [](std::string_view input, std::string_view search,
                         auto expected) {
            auto result = is_permuted_substring(input, search);
            printf("%s: is_permuted_substring(\"%.*s\", \"%.*s\") => %s\n",
                   result == expected ? "PASS" : "FAIL", (int)input.length(),
                   input.data(), (int)search.length(), search.data(),
                   result ? "T" : "F");
          };
        
          test("", "", true);
          test("", "a", false);
          test("a", "a", true);
          test("ab", "ab", true);
          test("ab", "ba", true);
          test("aba", "aa", false);
          test("baa", "aa", true);
          test("aacbba", "aab", false);
          test("encyclopedia", "dep", true);
          test("encyclopedia", "dop", false);
        
          constexpr char negative_input[]{-1, -2, -3, 0};
          constexpr char negative_search[]{-3, -2, 0};
          test(negative_input, negative_search, true);
        
          return 0;
        }
        

        【讨论】:

          【解决方案9】:

          我参加这个聚会迟到了……

          在第 70 页名为 Cracking the Coding Interview, 6th Edition 的书中也讨论了这个问题。作者说有可能使用 O(n) time complexity(线性)找到所有排列,但她没有编写算法,所以我认为我应该给出去吧。

          这是 C# 解决方案,以防万一有人在寻找......

          另外,我认为(不是 100% 肯定)它使用 O(n) time 复杂性找到排列的计数。

          public int PermutationOfPatternInString(string text, string pattern)
          {
              int matchCount = 0;
              Dictionary<char, int> charCount = new Dictionary<char, int>();
              int patLen = pattern.Length;
              foreach (char c in pattern)
              {
                  if (charCount.ContainsKey(c))
                  {
                      charCount[c]++;
                  }
                  else
                  {
                      charCount.Add(c, 1);
                  }
              }
          
              var subStringCharCount = new Dictionary<char, int>();
          
              // loop through each character in the given text (string)....
              for (int i = 0; i <= text.Length - patLen; i++)
              {
                  // check if current char and current + length of pattern-th char are in the pattern.
                  if (charCount.ContainsKey(text[i]) && charCount.ContainsKey(text[i + patLen - 1]))
                  {
                      string subString = text.Substring(i, patLen);
                      int j = 0;
                      for (; j < patLen; j++)
                      {
                          // there is no point going on if this subString doesnt contain chars that are in pattern...
                          if (charCount.ContainsKey(subString[j]))
                          {
                              if (subStringCharCount.ContainsKey(subString[j]))
                              {
                                  subStringCharCount[subString[j]]++;
                              }
                              else
                              {
                                  subStringCharCount.Add(subString[j], 1);
                              }
                          }
                          else
                          {
                              // if any of the chars dont appear in the subString that we are looking for
                              // break this loop and continue...
                              break;
                          }
                      }
          
                      int x = 0;
          
                      // this will be true only when we have current subString's permutation count
                      // matched with pattern's.
                      // we need this because the char count could be different 
                      if (subStringCharCount.Count == charCount.Count)
                      {
                          for (; x < patLen; x++)
                          {
                              if (subStringCharCount[subString[x]] != charCount[subString[x]])
                              {
                                  // if any count dont match then we break from this loop and continue...
                                  break;
                              }
                          }
                      }
          
                      if (x == patLen)
                      {
                          // this means we have found a permutation of pattern in the text...
                          // increment the counter.
                          matchCount++;
                      }
          
                      subStringCharCount.Clear(); // clear the count map.
                  }
              }
          
              return matchCount;
          }
          

          这里是单元测试方法...

          [TestCase("encyclopedia", "dep", 1)]
          [TestCase("cbabadcbbabbcbabaabccbabc", "abbc", 7)]
          [TestCase("xyabxxbcbaxeabbxebbca", "abbc", 2)]
          public void PermutationOfStringInText(string text, string pattern, int expectedAnswer)
          {
              int answer = runner.PermutationOfPatternInString(text, pattern);
              Assert.AreEqual(expectedAnswer, answer);
          }
          

          【讨论】:

            【解决方案10】:

            采用 O(TEXT.length) 运行时复杂度。

            当计算平均值时,其中一些解决方案将不起作用 文本值与计算的模式值的平均值匹配。前 - uw 和 五。虽然它们不匹配,但上面的一些解决方案仍然返回 真的。

            public static void main(String[] args) {
                String pattern = "dep";
                String text = "encyclopedia";
                System.out.println(isPermutationAvailable(pattern, text));
            }
            
            public static boolean isPermutationAvailable(String pattern, String text) {
                if (pattern.length() > text.length()) {
                    return false;
                }
                int[] patternMap = new int[26];
                int[] textMap = new int[26];
                for (int i = 0; i < pattern.length(); i++) {
                    patternMap[pattern.charAt(i) - 'a']++;
                    textMap[text.charAt(i) - 'a']++;
                }
                int count = 0;
                for (int i = 0; i < 26; i++) {
                    if (patternMap[i] == textMap[i]) {
                        count++;
                    }
                }
                for (int i = 0; i < text.length() - pattern.length(); i++) {
                    int r = text.charAt(i + pattern.length()) - 'a';
                    int l = text.charAt(i) - 'a';
                    if (count == 26) {
                        return true;
                    }
            
                    textMap[r]++;
                    if (textMap[r] == patternMap[r]) {
                        count++;
                    }
                    else if (textMap[r] == patternMap[r] + 1) {
                        count--;
                    }
            
                    textMap[l]--;
                    if (textMap[l] == patternMap[l]) {
                        count++;
                    }
                    else if (textMap[l] == patternMap[l] - 1) {
                        count--;
                    }
                }
                return count == 26;
            }
            

            【讨论】:

              【解决方案11】:

              基于检查窗口大小为短的较长字符串。由于排列只会改变字符的位置,所以排序后的字符串总是相同的。

              
                      #include <iostream>
                      #include <bits/stdc++.h>
                      using namespace std;
                      
                      int main ()
                      {
                        string shortone =  "abbc";
                        string longone ="cbabadcbbabbcbabaabccbabc";
                      
                        int s_length = shortone.length ();
                        int l_length = longone.length ();
                        string sub_string;
                        string unsorted_substring; // only for printing
                      
                        // sort the short one
                        sort (shortone.begin (), shortone.end ());
                      
                        if (l_length > s_length)
                          {
                                    for (int i = 0; i <= l_length - s_length; i++){
                                  
                                        sub_string = "";
                                        
                                        //Move the window
                                        sub_string = longone.substr (i, s_length);
                                        unsorted_substring=sub_string;
                                        
                                        // sort the substring window 
                                        sort (sub_string.begin (), sub_string.end ());
                                        if (shortone == sub_string)
                                          {
                                            cout << "substring is :" << unsorted_substring << '\t' <<
                                          "match found at the position: " << i << endl;
                                          }
                              
                              
                                  }
                          }
                        return 0;
                      }
              
              
              

              【讨论】:

              • 我不清楚为什么对短字符串进行排序会有帮助。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-09-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-03-09
              相关资源
              最近更新 更多