匹配String1中String2的字符的出现和模式

时间:2011-05-03 02:34:30

标签: java string algorithm

我在暑期实习的电话采访中被问到这个问题,并试图在Java中提出一个非常复杂的解决方案(虽然它也不准确)。

我有一个带2个字符串的函数,假设是“common”和“cmn”。它应该基于“c”,“m”,“n”在“common”中以相同顺序发生的事实返回True。但是如果参数是“共同的”和“omn”,它将返回False,因为即使它们以相同的顺序发生,但是'm'也出现在'o'之后(它没有模式匹配条件)

我使用Hashmaps和Ascii数组已经完成了它,但还没有得到令人信服的解决方案!从我读到现在,它可以与Boyer-Moore或Levenshtein距离算法有关吗?

希望在stackoverflow上暂时休息! :)

编辑:有些答案谈论减少字长或创建哈希集。但根据我的理解,这个问题不能用hashsets来完成,因为第一个字符串中每个字符的出现/重复都有其自身的意义。通过条件 - “con”,“cmn”,“cm”,“cn”,“mn”,“on”,“co”。失败的条件可能看起来不是 - “com”,“omn”,“mon”,“om”。这些是FALSE / FAIL,因为“o”发生在“m”之前和之后。另一个例子 - “google”,“ole”会PASS,但“google”,“gol”会失败,因为“o”也出现在“g”之前!

8 个答案:

答案 0 :(得分:4)

我认为这很简单。运行模式,然后每个字符获取它在字符串中最后一次出现的索引。索引必须始终增加,否则返回false。 所以在伪代码中:

index = -1
foreach c in pattern
    checkindex = string.lastIndexOf(c)
    if checkindex == -1                   //not found
        return false
    if checkindex < index
        return false
    if string.firstIndexOf(c) < index     //characters in the wrong order
        return false
    index = checkindex
return true

编辑:您可以通过将index作为起始索引传递给lastIndexOf方法来进一步改进代码。然后,您不必将checkindexindex进行比较,算法会更快。

更新:修正了算法中的错误。添加附加条件以考虑模式中字母的顺序。

答案 1 :(得分:2)

一个很好的问题和几个小时的研究,我想我找到了解决方案。首先让我尝试用不同的方法解释这个问题。

<强>要求:

让我们考虑相同的例子'common'(mainString)和'cmn'(subString)。首先我们需要清楚的是,任何字符都可以在mainString和subString中重复,并且由于我们专注于它的模式,角色的索引起着重要的作用。所以我们需要知道:

  • 角色的索引(最低和最高)

让我们保持这种状态并继续检查模式。对于common这个词,我们需要找出特定模式cmn是否存在。可能的共同点是: - (优先权适用)

  • c - &gt; Ø
  • c - &gt;米
  • c - &gt; Ñ
  • o - &gt;米
  • o - &gt; Ø
  • o - &gt; n
  • m - &gt;米
  • m - &gt; Ø
  • m - &gt; Ñ
  • o - &gt; Ñ

在任何时候,此优先级和比较必须有效。由于优先级起着重要作用,我们需要拥有每个唯一字符的索引而不是存储不同的模式。

<强>解决方案

解决方案的第一部分是使用以下标准创建哈希表: -

  1. 使用键作为mainString
  2. 的每个字符创建一个哈希表
  3. 哈希表中唯一键的每个条目都将存储两个索引,即lowerIndex和higherIndex
  4. 遍历mainString,对于每个新字符,使用mainString中字符的当前索引将lowerIndex的新条目更新到Hash中。
  5. 如果发生碰撞,请使用higherIndex条目更新当前索引,直到String
  6. 结束

    模式匹配的第二个主要部分: -

    1. 将标记设为错误
    2. 遍历subString并为 每个角色都是关键,后退 哈希的细节。
    3. 为下一个角色做同样的事。
    4. 在循环增量之前,验证两个条件

      If highestIndex(current character) > highestIndex(next character) Then
         Pattern Fails, Flag <- False, Terminate Loop
         // This condition is applicable for almost all the cases for pattern matching
      
      Else If lowestIndex(current character) > lowestIndex(next character) Then
         Pattern Fails, Flag <- False, Terminate Loop
         // This case is explicitly for cases in which patterns like 'mon' appear
      
    5. 显示标志

    6. N.B:由于我在Java方面不是那么多才多艺,所以我没有提交代码。但有些人可以尝试实现我的想法

答案 2 :(得分:1)

我自己以低效的方式完成了这个问题,但确实给出了准确的结果!如果有人能从中找到一个有效的代码/算法,我将不胜感激!

创建一个函数“Check”,它将2个字符串作为参数。检查字符串1中字符串2的每个字符.S1中每个字符的出现顺序应在S1中验证为真。

  1. 从字符串p中取出字符0并遍历字符串s以查找其首次出现的索引。
  2. 遍历填充的ascii数组,以查找超过第一次出现的索引的任何值。
  3. 进一步遍历以查找最后一次出现,并更新ascii数组
  4. 从字符串p中取字符1并遍历字符串s以查找字符串s中第一次出现的索引
  5. 遍历填充的ascii数组,以查找超过第一次出现的索引的任何值。如果找到,返回False。
  6. 进一步遍历以查找最后一次出现,并更新ascii数组
  7. 可以看出,这是一种强力方法......我猜O(N ^ 3)

    public class Interview
    {
        public static void main(String[] args)
    {
        if (check("google", "oge"))
            System.out.println("yes");
        else System.out.println("sorry!");
    }
    
     public static boolean check (String s, String p) 
    {   
    
         int[] asciiArr =  new int[256];    
         for(int pIndex=0; pIndex<p.length(); pIndex++) //Loop1 inside p
         {
            for(int sIndex=0; sIndex<s.length(); sIndex++) //Loop2 inside s
            {
                if(p.charAt(pIndex) == s.charAt(sIndex))    
                {
                    asciiArr[s.charAt(sIndex)] = sIndex; //adding char from s to its Ascii value
    
                    for(int ascIndex=0; ascIndex<256; )     //Loop 3 for Ascii Array
                    {
                        if(asciiArr[ascIndex]>sIndex)           //condition to check repetition
                        return false;
                        else ascIndex++;
                    }
                }
            }
         }
        return true;
    }
    }
    

答案 3 :(得分:0)

O(n log n)不可行吗?

步骤1,通过消除右侧显示的所有字符来减少字符串。严格来说,如果字符出现在您正在检查的字符串中,则只需要消除字符。

/** Reduces the maximal subsequence of characters in container that contains no
 * character from container that appears to the left of the same character in
 * container.  E.g. "common" -> "cmon", and "whirlygig" -> "whrlyig".
 */
static String reduceContainer(String container) {
  SparseVector charsToRight = new SparseVector();  // Like a Bitfield but sparse.
  StringBuilder reduced = new StringBuilder();
  for (int i = container.length(); --i >= 0;) {
    char ch = container.charAt(i);
    if (charsToRight.add(ch)) {
      reduced.append(ch);
    }
  }
  return reduced.reverse().toString();
}

步骤2,检查遏制。

static boolean containsInOrder(String container, String containee) {
  int containerIdx = 0, containeeIdx = 0;
  int containerLen = container.length(), containeeLen == containee.length();
  while (containerIdx < containerLen && containeeIdx < containeeLen) {
    // Could loop over codepoints instead of code-units, but you get the point...
    if (container.charAt(containerIdx) == containee.charAt(containeeIdx)) {
      ++containeeIdx;
    }
    ++containerIdx;
  }
  return containeeIdx == containeeLen;
}

要回答你的第二个问题,不,Levenshtein距离对你不会有帮助,因为它有一个属性,如果你交换参数输出是相同的,但你想要的算法不会。

答案 4 :(得分:0)

public class StringPattern {
    public static void main(String[] args) {
        String inputContainer = "common";
        String inputContainees[] = { "cmn", "omn" };
        for (String containee : inputContainees)
            System.out.println(inputContainer + " " + containee + " "
                    + containsCommonCharsInOrder(inputContainer, containee));

    }

    static boolean containsCommonCharsInOrder(String container, String containee) {
        Set<Character> containerSet = new LinkedHashSet<Character>() {
            // To rearrange the order
            @Override
            public boolean add(Character arg0) {
                if (this.contains(arg0))
                    this.remove(arg0);
                return super.add(arg0);
            }
        };
        addAllPrimitiveCharsToSet(containerSet, container.toCharArray());
        Set<Character> containeeSet = new LinkedHashSet<Character>();
        addAllPrimitiveCharsToSet(containeeSet, containee.toCharArray());

        // retains the common chars in order
        containerSet.retainAll(containeeSet);
        return containerSet.toString().equals(containeeSet.toString());

    }

    static void addAllPrimitiveCharsToSet(Set<Character> set, char[] arr) {
        for (char ch : arr)
            set.add(ch);
    }

}

<强>输出:

common cmn true
common omn false

答案 5 :(得分:0)

我认为这是我编写过的最糟糕的代码之一,或者是stackoverflow中最糟糕的代码示例之一......但是猜猜看......满足您的所有条件!
没有算法可以真正满足需要,所以我只是使用了暴力...测试它...
而且我可能只关心空间和时间的复杂性......我的目标是首先尝试解决它......并且可能会在以后改进它!

public class SubString {

    public static void main(String[] args) {
        SubString ss = new SubString();
        String[] trueconditions = {"con", "cmn", "cm", "cn", "mn", "on", "co" };
        String[] falseconditions = {"com", "omn", "mon", "om"};

        System.out.println("True Conditions : ");
        for (String str : trueconditions) {
            System.out.println("SubString? : " + str + " : " + ss.test("common", str));
        }
        System.out.println("False Conditions : ");
        for (String str : falseconditions) {
            System.out.println("SubString? : " + str + " : " + ss.test("common", str));
        }
        System.out.println("SubString? : ole : " + ss.test("google", "ole"));
        System.out.println("SubString? : gol : " + ss.test("google", "gol"));
    }

    public boolean test(String original, String match) {
        char[] original_array = original.toCharArray();
        char[] match_array = match.toCharArray();

        int[] value = new int[match_array.length];
        int index = 0;
        for (int i = 0; i < match_array.length; i++) {
            for (int j = index; j < original_array.length; j++) {
                if (original_array[j] != original_array[j == 0 ? j : j-1] && contains(match.substring(0, i), original_array[j])) {

                    value[i] = 2;
                } else {
                    if (match_array[i] == original_array[j]) {
                        if (value[i] == 0) {
                            if (contains(original.substring(0, j == 0 ? j : j-1), match_array[i])) {
                                value[i] = 2;
                            } else {
                                value[i] = 1;
                            }
                        }
                        index = j + 1;
                    }
                }
            }
        }
        for (int b : value) {
            if (b != 1) {
                return false;
            }
        }
        return true;
    }

    public boolean contains(String subStr, char ch) {
        for (char c : subStr.toCharArray()) {
            if (ch == c) {
                return true;
            }
        }
        return false;
    }
}

-IvarD

答案 6 :(得分:0)

我认为这不是对您的计算机科学基础的测试,更多的是您在Java编程环境中的实际操作。

可能在第二个参数中构建正则表达式,即......

omn -> o.*m[^o]*n

...然后使用String.matches(...)或使用Pattern类测试候选字符串。

在通用形式中,RegExp的构造应遵循以下几行。

  

exp - &gt; in [0] 。* + for each x:2 - &gt; in.lenght {( in [x-1] +    [^ [x-2]] * + [strong> in [x] )}

例如:

  

demmn - &gt; d。* E [^ d] *米[^ E] * M [^ M] * N

答案 7 :(得分:0)

我以不同的方式尝试自己。只是分享我的解决方案。

公共类PatternMatch {

public static boolean matchPattern(String str, String pat) {

    int slen = str.length();
    int plen = pat.length();
    int prevInd = -1, curInd;
    int count = 0;

    for (int i = 0; i < slen; i++) {

        curInd = pat.indexOf(str.charAt(i));


        if (curInd != -1) {

            if(prevInd == curInd)
                continue;
            else if(curInd == (prevInd+1))
                count++;
            else if(curInd == 0)
                count = 1;
            else count = 0;

            prevInd = curInd;
        }


        if(count == plen)
            return true;

    }

    return false;
}

public static void main(String[] args) {

    boolean r = matchPattern("common", "on");
    System.out.println(r);
}

}