私の友人は、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つの文字が同じ部分文字列を表すことはありません。また、文字は空の文字列を表すことはできません。
バックリファレンスを使用してパターンを正規表現に変換する必要があるだけではありませんか。つまり、次のようなものです(「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 を参照してください。
私が考えることができる最も簡単な解決策は、与えられた文字列を4つの部分に分割し、個々の部分を比較することです。 a
またはb
の長さはわかりませんが、a
sはb
sと同じ長さです。したがって、指定された文字列を分割する方法の数はそれほど多くありません。
例:パターン= [a b a b]
、与えられた文字列= redblueredblue
(合計14文字)
|a|
(a
の長さ)= 1の場合、a
sには2文字、b
sには12文字が残ります。つまり、|b|
= 6.分割された文字列= r edblue r edblue
。おっと、これはすぐに一致します。|a| = 2, |b| = 5
->分割された文字列= re dblue re dblue
->一致例2:パターン= [a b a b]
、文字列= redbluebluered
(合計14文字)
|a| = 1, |b| = 6
->分割された文字列= r edblue b luered
->一致しません|a| = 2, |b| = 5
->分割された文字列= re dblue bl uered
->一致しません|a| = 3, |b| = 4
->分割された文字列= red blue blu ered
->一致しません残りを確認する必要はありません。a
をb
に切り替えた場合とその逆の場合も同じです。
[a b c a b c]のパターンは何ですか?
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;
}
}
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つのブルートフォース再帰ソリューション:
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;
}
}
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;
}
};
私の解決策、両面ハッシュマップが必要であり、ハッシュマップの数も数える必要があるため
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
プレーンブルートフォース、最適化が可能かどうかはここではわかりません。
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"));
}
}
@EricM
私はあなたのDFSソリューションをテストしましたが、ケースのようにそれは間違っているようです:
パターン= ["a"、 "b"、 "a"]、s = "patrpatrr"
問題は、dictに既に存在するパターンに出会い、次の文字列に適合しない場合、削除して新しい値を割り当てようとすることです。ただし、このパターンを、それが発生する以前の時間の新しい値でチェックしていません。
私のアイデアは、追加の辞書(またはこの辞書にマージ)を提供して、それが最初に表示されたときに追跡する新しい値を提供し、別のスタックが私が遭遇する一意のパターンを追跡することです。 「一致しない」が発生すると、最後のパターンに問題があることがわかり、スタックからポップして、対応するdictの値を変更します。また、対応するインデックスで再度チェックを開始します。これ以上変更できない場合。スタックがなくなるまでポップし、Falseを返します。
(コメントを追加したいのですが、新しいユーザーとしての評判が足りません。実装していませんが、今のところロジックにエラーはありません。解決策に問題があるとすみません。 ==後で実装しようとします。)
C++での解決策を探している場合は、ここにブルートフォースの解決策があります。 https://linzhongzl.wordpress.com/2014/11/04/repeating-pattern-match/
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);
}
pattern
-"アバ"; input
-「redbluebluered」
pattern
内のすべての一意の文字のカウントを検索し、リスト_pattern_count
_に割り当てます。例:a
およびb
の_[2,2]
_。pattern_lengths
_を割り当てます。例:_[1,1]
_。pattern_lengths
_の値を右から左に維持する方程式を反復します:pattern_count * (pattern_lengths)^T = length(input)
(ベクトルのドット積)。ステップを使用して、次の方程式のルートに直接ジャンプします。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による実装)。
私はブルートフォースソリューションよりもはるかに良いとは思えません: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;
}
}