web-dev-qa-db-ja.com

指定された文字列が指定されたパターンに従っているかどうかを確認します

私の友人は、Googleで面接を受けただけで、この質問の解決策を提供できなかったために拒否されました。

私は数日で自分の面接をしていて、それを解決する方法を理解できないようです。

ここに質問があります:

[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)には任意の数の文字を含めることができます。 2つの文字が同じ部分文字列を表すことはありません。また、文字は空の文字列を表すことはできません。

20
SinnerShanky

バックリファレンスを使用してパターンを正規表現に変換する必要があるだけではありませんか。つまり、次のようなものです(「re」モジュールがロードされたPython 3)。

>>> 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 を参照してください。

9
EricM

私が考えることができる最も簡単な解決策は、与えられた文字列を4つの部分に分割し、個々の部分を比較することです。 aまたはbの長さはわかりませんが、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->一致しません

残りを確認する必要はありません。abに切り替えた場合とその逆の場合も同じです。

[a b c a b c]のパターンは何ですか?

18
zegkljan

Javaバックトラッキングソリューションです ソースリンク

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;
 }

}
2
Joker

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;

    }
1
Don Bar

もう1つのブルートフォース再帰ソリューション:

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;
    }
}
1
Roman
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;
  }
};

私の解決策、両面ハッシュマップが必要であり、ハッシュマップの数も数える必要があるため

0
richardzrc

Regexenを使用して言語生成の問題としてこれを解決しました。

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
0
IknoweD

プレーンブルートフォース、最適化が可能かどうかはここではわかりません。

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"));
   }
}
0
Amit

@EricM

私はあなたのDFSソリューションをテストしましたが、ケースのようにそれは間違っているようです:

パターン= ["a"、 "b"、 "a"]、s = "patrpatrr"

問題は、dictに既に存在するパターンに出会い、次の文字列に適合しない場合、削除して新しい値を割り当てようとすることです。ただし、このパターンを、それが発生する以前の時間の新しい値でチェックしていません。

私のアイデアは、追加の辞書(またはこの辞書にマージ)を提供して、それが最初に表示されたときに追跡する新しい値を提供し、別のスタックが私が遭遇する一意のパターンを追跡することです。 「一致しない」が発生すると、最後のパターンに問題があることがわかり、スタックからポップして、対応するdictの値を変更します。また、対応するインデックスで再度チェックを開始します。これ以上変更できない場合。スタックがなくなるまでポップし、Falseを返します。

(コメントを追加したいのですが、新しいユーザーとしての評判が足りません。実装していませんが、今のところロジックにエラーはありません。解決策に問題があるとすみません。 ==後で実装しようとします。)

0
Jamie

C++での解決策を探している場合は、ここにブルートフォースの解決策があります。 https://linzhongzl.wordpress.com/2014/11/04/repeating-pattern-match/

0
PiyushW

私の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);

}

0
Regina Kreimer

pattern-"アバ"; input-「redbluebluered」

  1. pattern内のすべての一意の文字のカウントを検索し、リスト_pattern_count_に割り当てます。例:aおよびbの_[2,2]_。
  2. 一意の文字ごとに_pattern_lengths_を割り当てます。例:_[1,1]_。
  3. _pattern_lengths_の値を右から左に維持する方程式を反復します:pattern_count * (pattern_lengths)^T = length(input)(ベクトルのドット積)。ステップを使用して、次の方程式のルートに直接ジャンプします。
  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による実装)。

0
apatsekin

私はブルートフォースソリューションよりもはるかに良いとは思えません:Wordの可能なすべてのパーティション分割を試してください(これは基本的に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;
    }
}
0
John Kurlak