字符串模式匹配

时间:2013-10-22 18:19:06

标签: java string algorithm

我无法弄清楚如何解决这个问题:

给出两个字符串,一个表示模式,一个是随机字符串,确定它是否与第一个字符串匹配

前:

string1: "aaba"
string2: "catcatdogcat"

因此,string1和string2是模式匹配的

如果string2是"catcatcatcat",则不会进行模式匹配。

对任何模式和字符串执行此操作。

我知道这是递归,但我很坚持......如何解决这个问题

3 个答案:

答案 0 :(得分:2)

好的,我会尝试解释这个递归,听起来没错,但我没有机会测试它(不在家里)。

采用向量v ['字母大小'],其中v [i] = string2中的字母数=字符串1中的字母i。

在你的情况下,最终是:v ['a'] = 3,v [b] = 3;

使用1初始化矢量。

对于rec函数:

你从string1获取第一个字母:a; 表示来自string2的字符串是从string2开始并以string2 + v ['a']结束的字符串;这是'c'; 你现在检查这是否是一个有效的解决方案,它是。

然后你进入rec(string1 + 1),再写一次, 因为v ['a']仍然= 1然后你把第二个a as ='a'。 你检查这是否是一个有效的解决方案,并不是因为你已经将第一个a定义为'c'。 你回到递归中并递增v ['a'],从乞讨开始。

你接受string1的第一个字母:a; 从string2代表'ca',(现在v ['a'] = 2) 检查是否有效。 rec(string1 +1);

依旧...... 在某一点上你会达到v ['a'] = 3而v ['b'] = 3; 然后使用rec功能,您将找到解决方案。

我发现在一个交互式函数中更容易实现,但你说了一些关于递归的事情,所以是的。

答案 1 :(得分:2)

获取唯一字母的数量。然后,您希望使用以下约束迭代每个字母的所有可能长度组合:

  1. sum(字母的长度*字母的出现)必须是string2的长度
  2. 每个长度必须至少为1
  3. 即,对于2个唯一字母和字符串长度为4,可能的长度为:

    (1,3)和(2,2)

    从这里开始很简单。对于每个唯一的字母,您可以找到该字母必须代表给定字符串的字符串,因为您知道每个字母的长度。然后将每个字母映射到它必须表示的字符串,如果在任何时候字母对应于与之前的实例不匹配的字符串,那么就没有匹配。

    对于你的例子:

    string1: "aaba"
    string2: "catcatdogcat"
    

    这里,对于长度为(3,3)的迭代。因为我们知道a的长度为3,所以我们知道a的第一次迭代必须是“cat”。然后下一个a,对应于“cat”(仍有匹配)。然后接下来的3个必须对应b。这是第一个b,所以它可以匹配任何3个字符。然后再将最后一个匹配给猫,你就完成了。

    如果您希望a,b,c在@MartijnCourteaux评论中有所独特(现在您的问题我再次阅读),那么最后您可以检查地图中是否有常见值,如果有的话常见的价值就是你没有匹配。

    如果在任何迭代中都匹配,则字符串与模式匹配。如果在所有迭代中没有匹配,则只有没有匹配。

答案 2 :(得分:1)

这很容易实现:

正则表达式是要走的路。在Regex中,有一种称为反向引用的东西。反向引用需要以匹配相同的字符串,提到的匹配组已经匹配。即正则表达式^([ab])\\1$将匹配aabb等每个字符串。第一组匹配a或b - 但反向引用NEEDS匹配相同的东西,匹配组(在本例中为“1”)匹配。

因此,您需要做的就是:将基于字符串的模式转换为正则表达式模式。

示例:

String regex = "^([a-z]+)\\1([a-z]+)\\1$";
   Pattern p = Pattern.compile(regex);
   Matcher m = p.matcher("catcatdogcat");

   if (m.matches()){
     System.out.println("matches!");
     System.out.println(m.group(0));
     System.out.println(m.group(1));
     System.out.println(m.group(2));

   }else{
    System.out.println("no matches!");
   }

产生

matches!
catcatdogcat
cat
dog

这将完全匹配您的给定字符串“catcatdogcat”,而匹配组1为“cat”并匹配组2为“dog”。

您现在需要做的是:

  • 编写一个函数,用char检查字符串模式aaba char。
  • 首次出现的字母:将其替换为([a-z]+)并记下该匹配组的编号(数组,Hashmap,...)
  • 信件的任何进一步出现:将其替换为\\1(如果该信件的记录号码为1)
  • 使用^$
  • 包装结果

最后,您的字符串aaba将转换为^([a-z]+)\\1([a-z]+)\\1$并满足您的需求。模式abccba将成为正则表达式^([a-z]+)([a-z]+)([a-z]+)\\3\\2\\1$

最后使用匹配器检查给定的字符串。

此示例仅假定小写字符,但您可以对其进行扩展。

然而保持“+”是很重要的,因为“*”将允许零长度匹配,这将使你的正则表达式匹配所有时间。

提到的第二个例子:

import java.util.regex.*;

public class HelloWorld {
  public static void main(String[] args) {
   String regex = "^([a-z]+)([a-z]+)([a-z]+)\\3\\2\\1$";
   Pattern p = Pattern.compile(regex);
   Matcher m = p.matcher("catdogcowcowdogcat");

   if (m.matches()){
     System.out.println("matches!");
     System.out.println(m.group(0));
     System.out.println(m.group(1));
     System.out.println(m.group(2));
     System.out.println(m.group(3));

   }else{
    System.out.println("no matches!");
   }
  }
}

产生

matches!
catdogcowcowdogcat
cat
dog
cow

编辑:如果需要(即使它不符合您的要求 - 请参阅评论):

public static String convertToRegex(String pattern){
    String regex = "";
    Map<Character, Integer> refs = new HashMap<Character, Integer>();
    Integer i=1;
    for (Character c : pattern.toCharArray()){
      if (refs.containsKey(c)){
         //known.
         regex += "\\" + refs.get(c);
      }else{
         //unknown
         regex += "([a-z]+)";
         refs.put(c, i++);
      }
    }

    return "^" + regex + "$";
  }