检查两个字符串是否共享相同的重复字符模式

时间:2012-11-06 19:49:54

标签: c# regex

是否有一个有效的正则表达式断言两个字符串共享相同的重复字符模式。

("tree", "loaa") => true
("matter", "essare") => false
("paper", "mime") => false
("acquaintance", "mlswmodqmdlp") => true
("tree", "aoaa") => false

如果不通过正则表达式,我正在寻找最有效的方法来执行任务

5 个答案:

答案 0 :(得分:11)

最简单的方法可能是同时手动遍历这两个字符串并在您执行操作时构建一个字典(匹配相应的字符):

if(input1.Length != input2.Length)
    return false;
var characterMap = new Dictionary<char, char>();
for(int i = 0; i < input1.Length; i++)
{
    char char1 = input1[i];
    char char2 = input2[i];
    if(!characterMap.ContainsKey(char1))
    {
        if (characterMap.ContainsValue(char2))
            return false;
        characterMap[char1] = char2;
    }
    else
    {
        if(char2 != characterMap[char1])
            return false;
    }
}
return true;

以同样的方式构建正则表达式。对于单个比较来说,这当然不是更有效,但如果您想在将来针对多个字符串检查一个重复模式,它可能会很有用。这次我们将字符与其反向引用相关联。

var characterMap = new Dictionary<char, int>();
string regex = "^";
int nextBackreference = 1;
for(int i = 0; i < input.Length; i++)
{
    char character = input[i];
    if(!characterMap.ContainsKey(character))
    {
        regex += "(.)";
        characterMap[character] = nextBackreference;
        nextBackreference++;
    }
    else
    {
        regex += (@"\" + characterMap[character]);
    }
}
regex += "$";

对于matter,它会生成此正则表达式:^(.)(.)(.)\3(.)(.)$。对于acquaintance这个:^(.)(.)(.)(.)\1(.)(.)(.)\1\6\2(.)$。如果当然可以稍后优化这个正则表达式(例如,对于第二个^(.)(.)..\1.(.).\1\3\2$),但无论如何,这将给你一个可重用的正则表达式来检查这个特定的重复模式。

编辑:请注意,给定的正则表达式解决方案有一个警告。它允许将输入字符串中的多个字符映射到测试字符串中的单个字符(这与您的上一个示例相矛盾)。要获得正确的正则表达式解决方案,您必须更进一步禁止已匹配的字符。所以acquaintance必须生成这个可怕的正则表达式:

^(.)(?!\1)(.)(?!\1|\2)(.)(?!\1|\2|\3)(.)\1(?!\1|\2|\3|\4)(.)(?!\1|\2|\3|\4|\5)(.)(?!\1|\2|\3|\4|\5|\6)(.)\1\6\2(?!\1|\2|\3|\4|\5|\6|\7)(.)$

我想不出更简单的方法,因为你不能在(否定的)字符类中使用反向引用。所以,也许,如果你想要断言这一点,那么正则表达式最终不是最佳选择。

免责声明:我不是真正的.NET专家,所以这可能不是在构建字典或字符串时遍历数组的最佳实践。但我希望你能以此为出发点。

答案 1 :(得分:1)

我不知道如何使用正则表达式进行操作,但是在代码中我会一次运行两个字符串,比较我去建立一个比较列表:

t = l
r = o
e = a
etc.

在添加每个比较之前,我会检查列表中是否已存在第一个字符串中的字符。如果第二个字符串中的相应字符与比较列表不匹配,则字符串模式不匹配。

答案 2 :(得分:1)

编辑:接受的代码现在是正确的。这个将作为替代品留在这里(几乎在任何意义上都不太好)。

    private static List<int> FindIndices(string str, char c, int ind)
    {
        var retval = new List<int>();
        int last = ind, temp;
        while (0 < (temp = str.IndexOf(c, last)))
        {
            retval.Add(temp);
            last = temp + 1;
        }           
        return retval;
    }

    public static int[] CanonicalForm(string s)
    {
        string table = String.Empty;
        var res = new int[s.Length];
        int marker = 0;
        int lastInd;

        for(int i=0; i < s.Length-1; ++i)
        {
            if (table.Contains(s[i]))
                continue;

            table += s[i];              
            lastInd = i+1;

            if (s.IndexOf(s[i], lastInd) > 0)
                res[i] = ++marker;
            else
                continue;

            foreach (var v in FindIndices(s, s[i], lastInd))
                res[v] = marker;
        }
        return res;
    }

比较:

    public static bool ComparePatterns(string s1, string s2)
    {
        return ( (s1.Length == s2.Length) && CanonicalForm(s1).SequenceEqual(CanonicalForm(s2)) );
    }

所以重点是建立一个可以在以后比较的规范形式。这不是特别聪明,但确实给出了正确的结果。

答案 3 :(得分:1)

只因为我喜欢LINQ :: :)

void Main()
{
    Console.WriteLine(Map("tree") == Map("loaa"));
    Console.WriteLine(Map("matter") == Map("essare"));
    Console.WriteLine(Map("paper") == Map("mime"));
    Console.WriteLine(Map("acquaintance") == Map("mlswmodqmdlp"));
    Console.WriteLine(Map("tree") == Map("aoaa"));  
}

public string Map(string input)
{
    var seen = new Dictionary<char,int>();
    var index = 0;
    return string.Join(
      string.Empty, 
      input.Select(c =>seen.ContainsKey(c) ? seen[c] : seen[c] = index++));
}

答案 4 :(得分:0)

遇到了同样的问题。我为此写了一段python代码。这非常简单,无需导入其他模块。基本思想是利用ASCII字符及其对应的数值之间的关系将两个给定的字符串分别转换为新的模式字符串。最后比较两个模式字符串。

def SamePattern(s1, s2):
  i = j = 97
  p1 = p2 = ""

  for index1, l1 in enumerate(s1):
    if l1 not in s1[0:index1]:
      p1 += chr(i)
      i += 1
    else:
      p1 += chr(97 + s1.index(l1))

  for index2, l2 in enumerate(s2): 
    if l2 not in s2[0:index2]:
      p2 += chr(j)
      j += 1
    else:
      p2 += chr(97 + s2.index(l2))
      
  if p1 == p2:
    return True
  else:
    return False