web-dev-qa-db-fra.com

Vérifier si la chaîne donnée suit le modèle donné

Un de mes amis vient d'avoir son interview chez Google et a été rejeté car il ne pouvait pas donner de solution à cette question.

J'ai mon propre entretien dans quelques jours et je n'arrive pas à trouver un moyen de le résoudre.

Voici la question:

Vous recevez un modèle, tel que [a b a b]. Vous recevez également un chaîne, exemple "redblueredblue". J'ai besoin d'écrire un programme qui raconte si la chaîne suit le modèle donné ou non.

Quelques exemples: 

Pattern: [a b b a] String: catdogdogcat renvoie 1

Pattern: [a b a b] String: redblueredblue renvoie 1 

Modèle: [a b b a] Chaîne: redblueredblue renvoie 0

J'ai pensé à quelques approches, comme obtenir le nombre de caractères uniques dans le motif, puis trouver plusieurs sous-chaînes uniques de la chaîne, puis les comparer au motif à l'aide d'une table de hachage. Toutefois, cela s’avère problématique si la sous-chaîne de a fait partie de b.

Ce serait vraiment bien si l'un de vous pouvait m'aider avec ça. :)

METTRE À JOUR:

Ajout d'informations: le motif peut comporter un nombre quelconque de caractères (a-z). Deux caractères ne représenteront pas la même sous-chaîne. En outre, un caractère ne peut pas représenter une chaîne vide.

19
SinnerShanky

Ne vous contentez pas de traduire le motif en une expression rationnelle en utilisant des références arrières, c'est-à-dire quelque chose comme ceci (Python 3 avec le module "re" chargé):

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

L'expression rationnelle est assez simple à générer. Si vous avez besoin de prendre en charge plus de 9 backrefs, vous pouvez utiliser des groupes nommés - voir le Python regexp docs .

8
EricM

La solution la plus simple à laquelle je puisse penser consiste à diviser la chaîne donnée en quatre parties et à comparer les différentes parties. Vous ne savez pas combien de temps a ou b est, mais les deux as sont de la même longueur que bs. Le nombre de façons de diviser la chaîne donnée n’est donc pas très grand.

Exemple: Pattern = [a b a b], chaîne donnée = redblueredblue (14 caractères au total)

  1. |a| (longueur de a) = 1, cela donne 2 caractères pour as et 12 caractères sont laissés pour bs, c'est-à-dire |b| = 6. Chaîne divisée = r edblue r edblue. Whoa, cela correspond tout de suite!
  2. (par curiosité) |a| = 2, |b| = 5 -> chaîne divisée = re dblue re dblue -> correspondance

Exemple 2: Pattern = [a b a b], string = redbluebluered (14 caractères au total)

  1. |a| = 1, |b| = 6 -> chaîne divisée = r edblue b luered -> pas de correspondance
  2. |a| = 2, |b| = 5 -> chaîne divisée = re dblue bl uered -> pas de correspondance
  3. |a| = 3, |b| = 4 -> chaîne divisée = red blue blu ered -> pas de correspondance

Il n'est pas nécessaire de vérifier le reste, car si vous avez basculé a pour b et vice versa, la situation est identique.

Quel est le motif qui a [a b c a b c]?

15
zegkljan

Voici la solution de backtracking Java. Lien source

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
Show Stopper

Ma mise en œuvre sur C #. Essayé de chercher quelque chose de propre en C #, impossible à trouver Donc, je vais l'ajouter à ici.

   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

Une autre solution de récursion en force brute:

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

Ma solution, étant donné que la table de hachage à deux faces est nécessaire et qu'il faut également compter le nombre de cartes de hachage 

0
richardzrc

J'ai résolu ce problème en tant que problème de production linguistique en utilisant 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

Plain Brute Force, je ne sais pas si une optimisation est possible ici ..

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

J'ai testé votre solution DFS et elle semble fausse, comme dans le cas suivant:

modèle = ["a", "b", "a"], s = "patrpatrr"

Le problème est que, lorsque vous rencontrez un modèle qui existe déjà dans dict et que vous trouvez qu'il ne peut pas contenir la chaîne suivante, vous supprimez et essayez de lui attribuer une nouvelle valeur. Cependant, vous n'avez pas vérifié ce modèle avec la nouvelle valeur pour les moments précédents.

Mon idée est de fournir un ajout (ou une fusion dans ce dict), une nouvelle valeur pour garder une trace de sa première apparition et une autre pile pour garder une trace du motif unique que je rencontre. Quand "pas de correspondance" se produit, je saurai qu'il y a un problème avec le dernier motif, je le sors de la pile et modifie la valeur correspondante dans le dict, je vais aussi recommencer à vérifier à l'index correspondant. Si ne peut plus être modifié. Je vais sauter jusqu'à ce qu'il n'en reste plus dans la pile, puis renvoyer False.

(Je veux ajouter des commentaires, mais je n'ai pas assez de réputation en tant que nouvel utilisateur. Je ne l'ai pas implémenté, mais jusqu'à présent, je n'ai trouvé aucune erreur dans ma logique. Je suis désolé s'il y a un problème avec ma solution. == Je vais essayer de le mettre en oeuvre plus tard.)

0
Jamie

Si vous recherchez une solution en C++, voici une solution en force brute: https://linzhongzl.wordpress.com/2014/11/04/repeating-pattern-match/

0
PiyushW

Ma solution de script 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 - "abba"; input - "redbluebluered"

  1. Trouvez des comptes pour chaque caractère unique dans pattern, affectez à la liste pattern_count. Ex .: [2,2] pour a et b.
  2. Assignez pattern_lengths pour chaque caractère unique. Ex .: [1,1].
  3. Itérez les valeurs de pattern_lengths de droite à gauche en maintenant l'équation: pattern_count * (pattern_lengths)^T = length(input) (produit scalaire des vecteurs). Utilisez step pour passer directement à la racine de l’équation suivante. 
  4. Lorsque l'équation est vérifiée, vérifiez si la chaîne suit les modèles avec le pattern_lenghts actuel (check_combination())

Implémentation 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))

Sur l'exemple de modèle issu de ce code (20 caractères, 4 uniques), fonctionne 50000 fois plus rapidement que la bruteforce simple (DFS) avec récursivité (implémentation par @EricM); 30 fois plus rapide qu'une expression régulière (mise en œuvre par @IknoweD).

0
apatsekin

Je ne peux pas penser à beaucoup mieux que la solution de force brute: essayez chaque partitionnement possible de la Parole (c'est essentiellement ce que Jan a décrit).

La complexité d'exécution est O(n^(2m)), où m est la longueur du modèle et n est la longueur de la chaîne.

Voici à quoi ressemble le code correspondant (j'ai demandé à mon code de renvoyer le mappage réel au lieu de 0 ou 1. Modifier le code pour renvoyer 0 ou 1 est simple):

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