【问题标题】:Check if the given string follows the given pattern检查给定的字符串是否遵循给定的模式
【发布时间】:2014-12-29 10:21:09
【问题描述】:

我的一个朋友刚刚在谷歌面试,因为无法给出这个问题的答案而被拒绝了。

几天后我有自己的面试,似乎无法找到解决问题的方法。

问题来了:

给你一个模式,例如 [a b a b]。您还获得了一个 字符串,例如“redblueredblue”。我需要编写一个程序告诉 字符串是否遵循给定的模式。

几个例子:

模式:[a b b a] 字符串:catdogdogcat 返回 1

模式:[a b a b] 字符串:redblueredblue 返回 1

模式:[a b b a] 字符串:redblueredblue 返回 0

我想到了一些方法,比如获取模式中唯一字符的数量,然后找到字符串的许多唯一子字符串,然后使用哈希图与模式进行比较。但是,如果 a 的子字符串是 b 的一部分,这将是一个问题。

如果你们中的任何人能帮我解决这个问题,那就太好了。 :)

更新:

添加信息:模式中可以有任意数量的字符 (a-z)。两个字符不会代表相同的子字符串。另外,一个字符不能代表一个空字符串。

【问题讨论】:

  • 图案有什么限制吗?仅仅是符号的任意组合吗?
  • 与模式中特定字母匹配的任何字符串都可以为空吗?
  • 另外,模式字符串中有多少个可能的字符?总是a和b,还是可以有更多的字符?
  • 模式中可以有任意数量的字符。两个字符不会代表相同的子字符串。另外,一个字符不能代表一个空字符串。
  • 有一个简单的解决方案,它通过将字符串的所有分区枚举为子字符串并检查它是否与模式字符串匹配,但这可能需要模式字符串长度的指数时间。我很好奇是否有一种从根本上更快的方法,或者这是否已知是 NP 完全的(或 co-NP 完全的?)

标签: string algorithm dynamic-programming graph-algorithm


【解决方案1】:

基于Java解决方案的Python解决方案在:https://www.algo.monster/problems/word_pattern_ii

def helper(pattern, s, idxPattern, idxString, myMap, mySet):
    if (idxPattern == len(pattern)) and (idxString == len(s)):
        return True
    if (idxPattern >= len(pattern)) or (idxString >= len(s)):
        return False
    thisChar = pattern[idxPattern]
    #print ("At Char: ", thisChar, " at location: ", idxPattern)
    for idxK in range(idxString + 1, len(s) + 1):
        subString = s[idxString:idxK]
        if (thisChar not in myMap) and (subString not in mySet) :
            myMap[thisChar] = subString
            mySet.add(subString)
            # print ("Before Map {0}, Set: {1}".format(myMap, mySet))       
            if helper(pattern, s, idxPattern + 1, idxK, myMap, mySet):
                return True
            myMap.pop(thisChar)
            mySet.remove(subString)
            # print ("After Map {0}, Set: {1}".format(myMap, mySet))      
        elif (thisChar in myMap) and (myMap[thisChar] == subString):
            if helper(pattern, s, idxPattern + 1, idxK, myMap, mySet):
                return True    
      
def word_pattern_match(pattern: str, s: str) -> bool:
    # WRITE YOUR BRILLIANT CODE HERE
    print ("Pattern {0}, String {1}".format(pattern, s))
    if (len(pattern) == 0) and (len(s) == 0):
        return True
    if (len(pattern) == 0):
        return False
    myMap = dict()
    mySet = set()

    return helper(pattern, s, 0, 0, myMap, mySet)

if __name__ == '__main__':
    pattern = input()
    s = input()
    res = word_pattern_match(pattern, s)
    print('true' if res else 'false')

【讨论】:

    【解决方案2】:

    递归检查每个组合。

    #include <bits/stdc++.h>
    using namespace std;
    
    /**
     * Given a string and a pattern, check if the whole string is following the given pattern.
     * e.g.
     * string            pattern     return
     * redblueredblue     abab        a:red, b:blue  true
     * redbb               aba          false
     * 
     * Concept:
     * Recursively checking
     * point_pat:0 point_str:0 a:r point_pat:1 point_str:1 b:e/ed/edb...
     * point_pat:0 point_str:1 a:re point_pat:1 point_str:2 b:d/db/dbl...
     */
    
    bool isMatch(const string &str, const string &pattern, unordered_map<char, string> &match_table, int point_str, int point_pat)
    {
        if (point_pat >= pattern.size() && point_str >= str.size())
            return true;
        if (point_pat >= pattern.size() || point_str >= str.size())
            return false;
    
        if (match_table.count(pattern[point_pat]))
        {
            auto &match_str = match_table[pattern[point_pat]];
            if (str.substr(point_str, match_str.size()) == match_str)
                return isMatch(str, pattern, match_table, point_str + match_str.size(), point_pat + 1);
            else
                return false;
        }
        else
        {
            for (int len = 1; len <= str.size() - point_str; ++len)
            {
                match_table[pattern[point_pat]] = str.substr(point_str, len);
                if (isMatch(str, pattern, match_table, point_str + len, point_pat + 1))
                {
                    return true;
                }
            }
            return false;
        }
    }
    
    bool isMatch(const string &str, const string &pattern)
    {
        unordered_map<char, string> match_table;
    
        bool res = isMatch(str, pattern, match_table, 0, 0);
    
        for (const auto &p : match_table)
        {
            cout << p.first << " : " << p.second << "\n";
        }
        return res;
    }
    
    int main()
    {
        string str{"redblueredblue"}, pattern{"abab"};
        cout << isMatch(str, pattern) << "\n";
        cout << isMatch(str, "ab") << "\n";
        cout << isMatch(str, "ababa") << "\n";
        cout << isMatch(str, "cba") << "\n";
        cout << isMatch(str, "abcabc") << "\n";
        cout << isMatch("patrpatrr", "aba") << "\n";
    }
    

    【讨论】:

    • 输入失败:abab redblueredblue
    【解决方案3】:

    我用 Java 写的一个解决方案(基于 HackerRank Dropbox Challenge practice)。

    您可以使用DEBUG_VARIATIONSDEBUG_MATCH 标志来更好地了解算法的工作原理。

    现在可能为时已晚,但您可能想先尝试在 HackerRank 上解决问题,然后再阅读建议的解决方案! ;-)

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class Solution {
    
        private static final boolean DEBUG_VARIATIONS = false;
        private static final boolean DEBUG_MATCH = true;
    
        static int wordpattern(final String pattern, final String input) {
            if (pattern.length() == 1) {
                return 1;
            }
    
            final int nWords = pattern.length();
    
            final List<List<String>> lists = split(input, nWords);
    
            for (final List<String> words : lists) {
                if (DEBUG_VARIATIONS) {
                    System.out.print("-> ");
                    for (int i = 0; i < words.size(); i++) {
                        System.out.printf("%s ", words.get(i));
                    }
                    System.out.println();
                }
    
                if (matches(pattern, words)) {
                    return 1;
                }
            }
    
            return 0;
        }
    
        // Return every possible way to split 'input' into 'n' parts
        private static final List<List<String>> split(final String input, final int n) {
            final List<List<String>> variations = new ArrayList<>();
    
            // Stop recursion when n == 2
            if (n == 2) {
                for (int i = 1; i < input.length(); i++) {
                    final List<String> l = new ArrayList<>();
                    l.add(input.substring(0, i));
                    l.add(input.substring(i));
                    variations.add(l);
                }
                return variations;
            }
    
            for (int i = 1; i < input.length() - n + 1; i++) {
                final List<List<String>> result = split(input.substring(i), n - 1);
                for (List<String> l : result) {
                    l.add(0, input.substring(0, i));
                }
                variations.addAll(result);
            }
    
            return variations;
        }
    
        // Return 'true' if list of words matches patterns
        private static final boolean matches(final String pattern, final List<String> words) {
            final Map<String, String> patterns = new HashMap<>();
    
            for (int i = 0; i < pattern.length(); i++) {
                final String key = String.valueOf(pattern.charAt(i));
                final String value = words.get(i);
    
                boolean hasKey = patterns.containsKey(key);
                boolean hasValue = patterns.containsValue(value);
    
                if (!hasKey && !hasValue) {
                    patterns.put(key, value);
                } else if (hasKey && !hasValue) {
                    return false;
                } else if (!hasKey && hasValue) {
                    return false;
                } else if (hasKey && hasValue) {
                    if (!value.equals(patterns.get(key))) {
                        return false;
                    }
                }
            }
    
            if (DEBUG_MATCH) {
                System.out.print("Found match! -> ");
                for (int i = 0; i < words.size(); i++) {
                    System.out.printf("%s ", words.get(i));
                }
                System.out.println();
            }
    
            return true;
        }
    
        public static void main(final String[] args) {
            System.out.println(wordpattern("abba", "redbluebluered"));
        }
    }
    

    【讨论】:

      【解决方案4】:

      pattern - “阿爸”; input - “红蓝蓝红”

      1. 查找pattern 中每个唯一字符的计数,分配给列表pattern_count。例如:[2,2] 代表 ab
      2. 为每个唯一字符分配pattern_lengths。例如:[1,1]
      3. 迭代pattern_lengths从右到左维持方程的值: pattern_count * (pattern_lengths)^T = length(input)(向量的点积)。使用 step 直接跳转到下一个方程根。
      4. 当等式成立时,检查字符串是否遵循当前pattern_lenghts (check_combination()) 的模式

      Python 实现:

      def check(pattern, input):
          def _unique(pattern):
              hmap = {}
              for i in pattern:
                  if i not in hmap:
                      hmap[i] = 1
                  else:
                      hmap[i] += 1
              return hmap.keys(), hmap.values()
          def check_combination(pattern, string, pattern_unique, pattern_lengths):
              pos = 0
              hmap = {}
              _set = set()
              for code in pattern:
                  string_value = string[pos:pos + pattern_lengths[pattern_unique.index(code)]]
                  if code in hmap:
                      if hmap[code] != string_value:
                          return False
                  else:
                      if string_value in _set:
                          return False
                      _set.add(string_value)
                      hmap[code] = string_value
                  pos += len(string_value)
              return False if pos < len(string) else True
      
          pattern = list(pattern)
          pattern_unique, pattern_count = _unique(pattern)
          pattern_lengths = [1] * len(pattern_unique)
          x_len =  len(pattern_unique)
          i = x_len - 1
          while i>0:
              diff_sum_pattern = len(input) - sum([x * y for x, y in zip(pattern_lengths, pattern_count)])
              if diff_sum_pattern >= 0:
                  if diff_sum_pattern == 0 and \
                     check_combination(pattern, input, pattern_unique, pattern_lengths):
                          return 1
                  pattern_lengths[i] += max(1, diff_sum_pattern // pattern_count[i])
              else:
                  pattern_lengths[i:x_len] = [1] * (x_len - i)
                  pattern_lengths[i - 1] += 1
                  sum_pattern = sum([x * y for x, y in zip(pattern_lengths, pattern_count)])
                  if sum_pattern <= len(input):
                      i = x_len - 1
                  else:
                      i -= 1
                      continue
          return 0
      
      task = ("abcdddcbaaabcdddcbaa","redbluegreenyellowyellowyellowgreenblueredredredbluegreenyellowyellowyellowgreenblueredred")
      print(check(*task))
      

      在此代码中的示例模式(20 个字符,4 个唯一)上,使用递归(由 @EricM 实现)比普通暴力破解 (DFS) 快 50000 倍;比正则表达式快 30 倍(由@IknoweD 实现)。

      【讨论】:

      • 输入用例的代码失败:aaaa asdasdasdasd
      【解决方案5】:

      我使用正则表达式解决了这个语言生产问题。

      def  wordpattern( pattern,  string):
          '''
              input: pattern 'abba'
              string  'redbluebluered'
              output: 1 for match, 2 for no match
          '''
      
          # assemble regex into something like this for 'abba':
          # '^(?P<A>.+)(?P<B>.+)(?P=B)(?P=A)$'
          p = pattern
          for c in pattern:
              C = c.upper()
              p = p.replace(c,"(?P<{0}>.+)".format(C),1)
              p = p.replace(c,"(?P={0})".format(C),len(pattern))
          p = '^' + p + '$'
      
          # check for a preliminary match
          if re.search(p,string):
              rem = re.match(p,string)
              seen = {}
              # check to ensure that no points in the pattern share the same match
              for c in pattern:
                  s = rem.group(c.upper())
                  # has match been seen? yes, fail, no continue
                  if s in seen and seen[s] != c:
                      return 0
                  seen[s] = c
              # success
                  return  1
          # did not hit the search, fail
          return 0
      

      【讨论】:

        【解决方案6】:

        这里是java回溯解决方案。 Source link

        public class Solution {
        
        public boolean isMatch(String str, String pat) {
        Map<Character, String> map = new HashMap<>();
        return isMatch(str, 0, pat, 0, map);
         }
        
        boolean isMatch(String str, int i, String pat, int j, Map<Character,  String> map) {
        // base case
        if (i == str.length() && j == pat.length()) return true;
        if (i == str.length() || j == pat.length()) return false;
        
        // get current pattern character
        char c = pat.charAt(j);
        
        // if the pattern character exists
        if (map.containsKey(c)) {
          String s = map.get(c);
        
          // then check if we can use it to match str[i...i+s.length()]
          if (i + s.length() > str.length() || !str.substring(i, i + s.length()).equals(s)) {
            return false;
          }
        
          // if it can match, great, continue to match the rest
          return isMatch(str, i + s.length(), pat, j + 1, map);
        }
        
        // pattern character does not exist in the map
        for (int k = i; k < str.length(); k++) {
          // create or update the map
          map.put(c, str.substring(i, k + 1));
        
          // continue to match the rest
          if (isMatch(str, k + 1, pat, j + 1, map)) {
            return true;
          }
        }
        
        // we've tried our best but still no luck
        map.remove(c);
        
        return false;
         }
        
        }
        

        【讨论】:

          【解决方案7】:

          根据给出的模式,您可以回答“不同”的问题(实际上是同一个问题)。

          对于像 [a b b a] 这样的模式,判断字符串是否是回文。

          对于像 [a b a b] 这样的模式,确定字符串的后半部分是否等于字符串的前半部分。

          较长的模式,如 [a b c b c a],但您仍将其分解为更小的问题来解决。对于这个,您知道字符串的最后 n 个字符应该与前 n 个字符相反。一旦它们不再相等,您只需检查另一个 [b c b c] 问题。

          虽然有可能,但在一次采访中,我怀疑他们会给你提供比 3-4 个不同的子字符串更复杂的东西。

          【讨论】:

            【解决方案8】:

            我的java脚本解决方案:

            function isMatch(pattern, str){
            
              var map = {}; //store the pairs of pattern and strings
            
              function checkMatch(pattern, str) {
            
                if (pattern.length == 0 && str.length == 0){
                  return true;
                }
                //if the pattern or the string is empty
                if (pattern.length == 0 || str.length == 0){
                  return false;
                }
            
                //store the next pattern
                var currentPattern = pattern.charAt(0);
            
                if (currentPattern in map){
                    //the pattern has alredy seen, check if there is a match with the string
                    if (str.length >= map[currentPattern].length  && str.startsWith(map[currentPattern])){
                      //there is a match, try all other posibilities
                      return checkMatch(pattern.substring(1), str.substring(map[currentPattern].length));
                    } else {
                      //no match, return false
                      return false;
                    }
                }
            
                //the current pattern is new, try all the posibilities of current string
                for (var i=1; i <= str.length; i++){
                    var stringToCheck = str.substring(0, i);
            
                    //store in the map
                    map[currentPattern] = stringToCheck;
                    //try the rest
                    var match = checkMatch(pattern.substring(1), str.substring(i));
                    if (match){
                        //there is a match
                         return true;
                    } else {
                       //if there is no match, delete the pair from the map
                       delete map[currentPattern];
                    }
                }
                return false;
              }
            
              return checkMatch(pattern, str);
            

            }

            【讨论】:

            • 您的代码有错误。您的检查 str.length >= map[currentPattern].length && str.startsWith(map[currentPattern]) 不正确。请检查并更正解决方案。
            【解决方案9】:

            我在 C# 上的实现。试图在 C# 中寻找一些干净的东西,但找不到。所以我将它添加到这里。

               private static bool CheckIfStringFollowOrder(string text, string subString)
                {
                    int subStringLength = subString.Length;
            
                    if (text.Length < subStringLength) return false;
            
                    char x, y;
                    int indexX, indexY;
            
                    for (int i=0; i < subStringLength -1; i++)
                    {
                        indexX = -1;
                        indexY = -1;
            
                        x = subString[i];
                        y = subString[i + 1];
            
                        indexX = text.LastIndexOf(x);
                        indexY = text.IndexOf(y);
            
                        if (y < x || indexX == -1 || indexY == -1)
                            return false;
                    }
            
                    return true;
            
                }
            

            【讨论】:

            • 看起来你正在实现一些完全不同的东西。
            【解决方案10】:

            另一种暴力递归解决方案:

            import java.io.IOException;
            import java.util.*;
            
            public class Test {
            
                public static void main(String[] args) throws IOException {
                    int res;
                    res = wordpattern("abba", "redbluebluered");
                    System.out.println("RESULT: " + res);
                }
            
                static int wordpattern(String pattern, String input) {
                    int patternSize = 1;
                    boolean res = findPattern(pattern, input, new HashMap<Character, String>(), patternSize);
                    while (!res && patternSize < input.length())
                    {
                        patternSize++;
                        res = findPattern(pattern, input, new HashMap<Character, String>(), patternSize);
                    }
                    return res ? 1 : 0;
                }
            
                private static boolean findPattern(String pattern, String input, Map<Character, String> charToValue, int patternSize) {
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < pattern.length(); i++) {
                        char c = pattern.charAt(i);
                        if (charToValue.containsKey(c)) {
                            sb.append(charToValue.get(c));
                        } else {
                            // new character in pattern
                            if (sb.length() + patternSize > input.length()) {
                                return false;
                            } else {
                                String substring = input.substring(sb.length(), sb.length() + patternSize);
                                charToValue.put(c, substring);
                                int newPatternSize = 1;
                                boolean res = findPattern(pattern, input, new HashMap<>(charToValue), newPatternSize);
                                while (!res && newPatternSize + sb.length() + substring.length() < input.length() - 1) {
                                    newPatternSize++;
                                    res = findPattern(pattern, input, new HashMap<>(charToValue), newPatternSize);
                                }
                                return res;
                            }
                        }
                    }
                    return sb.toString().equals(input) && allValuesUniq(charToValue.values());
                }
            
                private static boolean allValuesUniq(Collection<String> values) {
                    Set<String> set = new HashSet<>();
                    for (String v : values) {
                        if (!set.add(v)) {
                            return false;
                        }
                    }
                    return true;
                }
            }
            

            【讨论】:

              【解决方案11】:

              我能想到的最简单的解决方案是将给定的字符串分成四个部分并比较各个部分。您不知道ab 有多长,但as 和bs 的长度相同。所以如何划分给定字符串的方法数量并不是很多。

              示例: 模式 = [a b a b],给定字符串 = redblueredblue(共 14 个字符)

              1. |a|a 的长度)= 1,那么as 有 2 个字符,bs 剩下 12 个字符,即|b| = 6。分割后的字符串 = r edblue r edblue。哇,这马上就匹配了!
              2. (只是出于好奇)|a| = 2, |b| = 5 -> 分割字符串 = re dblue re dblue -> 匹配

              示例 2: 模式 = [a b a b],字符串 = redbluebluered(共 14 个字符)

              1. |a| = 1, |b| = 6 -> 分割字符串 = r edblue b luered -> 不匹配
              2. |a| = 2, |b| = 5 -> 分割字符串 = re dblue bl uered -> 不匹配
              3. |a| = 3, |b| = 4 -> 分割字符串 = red blue blu ered -> 不匹配

              其余的不需要检查,因为如果你把a换成b,反之亦然,情况是一样的。

              有 [a b c a b c] 的模式是什么?

              【讨论】:

              • 但它适用于[a a b b][b b a a] 这样的模式吗?
              【解决方案12】:
              class StringPattern{
              public:
                int n, pn;
                string str;
                unordered_map<string, pair<string, int>> um;
                vector<string> p;
                bool match(string pat, string str_) {
                  p.clear();
                  istringstream istr(pat);
                  string x;
                  while(istr>>x) p.push_back(x);
                  pn=p.size();
                  str=str_;
                  n=str.size();
                  um.clear();
                  return dfs(0, 0);
                }
              
                bool dfs(int i, int c) {
                  if(i>=n) {
                    if(c>=pn){
                        return 1;
                    }
                  }
                  if(c>=pn) return 0;
                  for(int len=1; i+len-1<n; len++) {
                    string sub=str.substr(i, len);
              
              
                    if(um.count(p[c]) && um[p[c]].fi!=sub
                       || um.count(sub) && um[sub].fi!=p[c]
                       )
                        continue;
                    //cout<<"str:"<<endl;
                    //cout<<p[c]<<" "<<sub<<endl;
                    um[p[c]].fi=sub;
                    um[p[c]].se++;
                    um[sub].fi=p[c];
                    um[sub].se++;
                    //um[sub]=p[c];
                    if(dfs(i+len, c+1)) return 1;
                    um[p[c]].se--;
                    if(!um[p[c]].se) um.erase(p[c]);
                    um[sub].se--;
                    if(!um[sub].se) um.erase(sub);
                    //um.erase(sub);
                  }
                  return 0;
                }
              };
              

              我的解决方案,由于需要两侧的hashmap,并且还需要统计hash map的个数

              【讨论】:

                【解决方案13】:

                Plain Brute Force,不确定这里是否可以进行任何优化..

                import java.util.HashMap;
                import java.util.Map;
                import org.junit.*;
                
                public class Pattern {
                   private Map<Character, String> map;
                   private boolean matchInt(String pattern, String str) {
                      if (pattern.length() == 0) {
                         return str.length() == 0;
                      }
                      char pch = pattern.charAt(0);
                      for (int i = 0; i < str.length(); ++i) {
                         if (!map.containsKey(pch)) {
                            String val = str.substring(0, i + 1);
                            map.put(pch, val);
                            if (matchInt(pattern.substring(1), str.substring(val.length()))) {
                               return true;
                            } else {
                               map.remove(pch);
                            }
                         } else {
                            String val = map.get(pch);
                            if (!str.startsWith(val)) {
                               return false;
                            }
                            return matchInt(pattern.substring(1), str.substring(val.length()));
                         }
                      }
                      return false;
                   }
                   public boolean match(String pattern, String str) {
                      map = new HashMap<Character, String>();
                      return matchInt(pattern, str);
                   }
                   @Test
                   public void test1() {
                      Assert.assertTrue(match("aabb", "ABABCDCD"));
                      Assert.assertTrue(match("abba", "redbluebluered"));
                      Assert.assertTrue(match("abba", "asdasdasdasd"));
                      Assert.assertFalse(match("aabb", "xyzabcxzyabc"));
                      Assert.assertTrue(match("abba", "catdogdogcat"));
                      Assert.assertTrue(match("abab", "ryry"));
                      Assert.assertFalse(match("abba", " redblueredblue"));
                   }
                }
                

                【讨论】:

                  【解决方案14】:

                  如果您正在寻找 C++ 中的解决方案,这里有一个蛮力解决方案: https://linzhongzl.wordpress.com/2014/11/04/repeating-pattern-match/

                  【讨论】:

                    【解决方案15】:

                    我想不出比蛮力解决方案更好的办法:尝试对单词进行所有可能的划分(这基本上是 Jan 所描述的)。

                    运行时复杂度为O(n^(2m)),其中m 是模式的长度,n 是字符串的长度。

                    这是代码的样子(我让我的代码返回实际的映射,而不仅仅是 0 或 1。修改代码以返回 0 或 1 很容易):

                    import java.util.Arrays;
                    import java.util.ArrayDeque;
                    import java.util.ArrayList;
                    import java.util.Deque;
                    import java.util.HashMap;
                    import java.util.List;
                    import java.util.Map;
                    
                    public class StringBijection {
                        public static void main(String[] args) {
                            String chars = "abaac";
                            String string = "johnjohnnyjohnjohncodes";
                            List<String> stringBijection = getStringBijection(chars, string);
                    
                            System.out.println(Arrays.toString(stringBijection.toArray()));
                        }
                    
                        public static List<String> getStringBijection(String chars, String string) {
                            if (chars == null || string == null) {
                                return null;
                            }
                    
                            Map<Character, String> bijection = new HashMap<Character, String>();
                            Deque<String> assignments = new ArrayDeque<String>();
                            List<String> results = new ArrayList<String>();
                            boolean hasBijection = getStringBijection(chars, string, 0, 0, bijection, assignments);
                    
                            if (!hasBijection) {
                                return null;
                            }
                    
                            for (String result : assignments) {
                                results.add(result);
                            }
                    
                            return results;
                        }
                    
                        private static boolean getStringBijection(String chars, String string, int charIndex, int stringIndex, Map<Character, String> bijection, Deque<String> assignments) {
                            int charsLen = chars.length();
                            int stringLen = string.length();
                    
                            if (charIndex == charsLen && stringIndex == stringLen) {
                                return true;
                            } else if (charIndex == charsLen || stringIndex == stringLen) {
                                return false;
                            }
                    
                            char currentChar = chars.charAt(charIndex);
                            List<String> possibleWords = new ArrayList<String>();
                            boolean charAlreadyAssigned = bijection.containsKey(currentChar);
                    
                            if (charAlreadyAssigned) {
                                String word = bijection.get(currentChar);
                                possibleWords.add(word);
                            } else {
                                StringBuilder word = new StringBuilder();
                    
                                for (int i = stringIndex; i < stringLen; ++i) {
                                    word.append(string.charAt(i));
                                    possibleWords.add(word.toString());
                                }
                            }
                    
                            for (String word : possibleWords) {
                                int wordLen = word.length();
                                int endIndex = stringIndex + wordLen;
                    
                                if (endIndex <= stringLen && string.substring(stringIndex, endIndex).equals(word)) {
                                    if (!charAlreadyAssigned) {
                                        bijection.put(currentChar, word);
                                    }
                    
                                    assignments.addLast(word);
                    
                                    boolean done = getStringBijection(chars, string, charIndex + 1, stringIndex + wordLen, bijection, assignments);
                    
                                    if (done) {
                                        return true;
                                    }
                    
                                    assignments.removeLast();
                    
                                    if (!charAlreadyAssigned) {
                                        bijection.remove(currentChar);
                                    }
                                }
                            }
                    
                            return false;
                        }
                    }
                    

                    【讨论】:

                      【解决方案16】:

                      @EricM

                      我测试了你的 DFS 解决方案,它似乎是错误的,比如案例:

                      pattern = ["a", "b", "a"], s = "patrpatrr"

                      问题是当你遇到 dict 中已经存在的模式并发现它不适合下面的字符串时,你删除并尝试为其分配一个新值。但是,您还没有使用新值检查此模式之前出现的次数。

                      我的想法是提供添加字典(或在此字典中合并)新值以跟踪它第一次出现的时间,并提供另一个堆栈以跟踪我遇到的独特模式。当“不匹配”发生时,我会知道最后一个模式有问题,我将它从堆栈中弹出并修改 dict 中的相应值,我将再次开始检查相应的索引。如果不能再修改。我会弹出直到堆栈中没有剩余,然后返回 False。

                      (我想添加 cmets,但作为新用户没有足够的声誉。我还没有实现它,但到目前为止我还没有发现我的逻辑有任何错误。如果有问题,我很抱歉使用我的解决方案== 我稍后会尝试实现它。)

                      【讨论】:

                      • 确实,我推送的版本很垃圾。我添加了 Alexander Taylor 的箱子和你的箱子,修好并清理了一下。请参阅更新的要点。不过,我仍然更喜欢正则表达式的想法:正则表达式引擎为您执行 DFS,因此编写错误的风险更小:-D
                      【解决方案17】:

                      您是否只需要使用反向引用将模式转换为正则表达式,即类似这样的东西(Python 3 加载了“re”模块):

                      >>> print(re.match('(.+)(.+)\\2\\1', 'catdogdogcat'))
                      <_sre.SRE_Match object; span=(0, 12), match='catdogdogcat'>
                      
                      >>> print(re.match('(.+)(.+)\\1\\2', 'redblueredblue'))
                      <_sre.SRE_Match object; span=(0, 14), match='redblueredblue'>
                      
                      >>> print(re.match('(.+)(.+)\\2\\1', 'redblueredblue'))
                      None
                      

                      生成正则表达式看起来很简单。如果您需要支持超过 9 个反向引用,您可以使用命名组 - 请参阅 Python regexp docs

                      【讨论】:

                      • 这个解决方案的时间复杂度是多少?我担心这会奏效,但可能不会比蛮力方法快。
                      • 如果我希望 2 个名称组的字符串具有唯一值怎么办?
                      • @EricM,如果不允许使用正则表达式,该怎么办?
                      • @TimothyHa,像这样粗略的解决方案实现深度优先搜索:gist.github.com/EricMountain/51cb333297ef37230582
                      • 尝试在 gist 链接上运行,但在某些情况下它似乎给出了不正确的输出。这是我受您启发的答案:gist.github.com/alexsapps/83e0054672973672f39e
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-03-15
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2019-12-17
                      • 2016-06-07
                      • 1970-01-01
                      相关资源
                      最近更新 更多