匹配字符串中的子字符串,允许 1 个字符不匹配

Posted

技术标签:

【中文标题】匹配字符串中的子字符串,允许 1 个字符不匹配【英文标题】:Match sub-string within a string with tolerance of 1 character mismatch 【发布时间】:2011-03-20 13:33:48 【问题描述】:

我在 CareerCup.com 上浏览了一些亚马逊面试问题,遇到了一个有趣的问题,但我一直不知道该怎么做。从2天开始,我一直在考虑这个问题。要么我采取了一种方法,要么它是一个真正难以编写的函数。

问题如下:

用 C 语言编写一个函数,可以判断一个字符串是否是另一个字符串的子字符串。请注意,一个字符不匹配 应该被忽略。

A mismatch can be an extra character: ’dog’ matches ‘xxxdoogyyyy’  
A mismatch can be a missing character: ’dog’ matches ‘xxxdgyyyy’ 
A mismatch can be a different character: ’dog’ matches ‘xxxdigyyyy’

问题中没有提到返回值,所以我假设函数的签名可以是这样的:

char * MatchWithTolerance(const char * str, const char * substr);

如果与给定规则匹配,则返回指向字符串中匹配子字符串开头的指针。否则返回 null。

奖金

如果有人也能找到一种通用的方法来将容差设为 n 而不是 1,那就太好了。 在这种情况下,签名将是:

char * MatchWithTolerance(const char * str, const char * substr, unsigned int tolerance = 1);

感谢所有愿意尝试并分享他们成功解决方案的人。

【问题讨论】:

dog 是否也匹配 xxxdogyyy (删除了一组与问题无关的cmets) 【参考方案1】:

这似乎有效,如果您发现任何错误,请告诉我,我会尝试修复它们:

int findHelper(const char *str, const char *substr, int mustMatch = 0)

    if ( *substr == '\0' )
        return 1;

    if ( *str == '\0' )
        return 0;

    if ( *str == *substr )
        return findHelper(str + 1, substr + 1, mustMatch);
    else
    
        if ( mustMatch )
            return 0;

        if ( *(str + 1) == *substr )
            return findHelper(str + 1, substr, 1);
        else if ( *str == *(substr + 1) )
            return findHelper(str, substr + 1, 1);
        else if ( *(str + 1) == *(substr + 1) )
            return findHelper(str + 1, substr + 1, 1);
        else if ( *(substr + 1) == '\0' )
            return 1;
        else
            return 0;
    


int find(const char *str, const char *substr)

    int ok = 0;
    while ( *str != '\0' )
        ok |= findHelper(str++, substr, 0);

    return ok;



int main()

    printf("%d\n", find("xxxdoogyyyy", "dog"));
    printf("%d\n", find("xxxdgyyyy", "dog"));
    printf("%d\n", find("xxxdigyyyy", "dog"));

基本上,我确保只有一个字符可以不同,然后运行对大海捞针的每个后缀执行此操作的函数。

【讨论】:

+1!!这非常有效。我确信我们可以使这个返回指针指向匹配字符串的开头。这也可以通用。非常感谢!如果我遇到任何不匹配的情况,我会回复。 @IVlad 对每个 if else 语句的一些评论将有助于我们轻松理解它。请考虑这样做。【参考方案2】:

这与 IT 的一个经典问题有关,称为Levenshtein distance。 请参阅Wikibooks 了解一系列不同语言的实现。

【讨论】:

+1 表示没有编写明显会被抄袭的代码并作为他自己的提交。 不,据我所知,这与 levenshtein 距离无关。这要求子字符串匹配,其中允许 1 个字符不匹配。您如何建议我们为此使用 levenshtein 距离? 我应该更清楚一点。 Levenshtein 的实现并没有解决原来的问题。但是,查看算法,您会发现只需稍作改动即可实现模糊子字符串匹配。同样,互联网已经准备好实现:ginstrom.com/scribbles/2007/12/01/… @Stefan 你为什么在每条该死的评论中都挑剔我?我不会在任何地方提交此代码!你甚至没有正确阅读我的问题。我写道,我正在处理面试问题,发现这很有趣。我试图解决它,但不知何故失败了。为什么你认为我打算抄袭这个?这意味着您甚至不了解现实世界中的面试是如何进行的。没有公司会要求你在手机上写这么大的功能。他们只会要求你在 face-2-face interview 中写这样的代码,你显然不能从 Stack Overflow 复制粘贴。 @Pumbaa 感谢您的意见。我很感激。 +1【参考方案3】:

这与早期的解决方案略有不同,但我对这个问题很感兴趣并想试一试。显然如果需要优化,我只是想要一个解决方案。

char *match(char *str, char *substr, int tolerance)

  if (! *substr) return str;
  if (! *str) return NULL;

  while (*str)
  
    char *str_p;
    char *substr_p;
    char *matches_missing;
    char *matches_mismatched;

    str_p = str;
    substr_p = substr;

    while (*str_p && *substr_p && *str_p == *substr_p)
    
      str_p++;
      substr_p++;
    
    if (! *substr_p) return str;
    if (! tolerance)
    
      str++;
      continue;
    

    if (strlen(substr_p) <= tolerance) return str;

    /* missed due to a missing letter */
    matches_missing = match(str_p, substr_p + 1, tolerance - 1);
    if (matches_missing == str_p) return str;

    /* missed due to a mismatch of letters */
    matches_mismatched = match(str_p + 1, substr_p + 1, tolerance - 1);
    if (matches_mismatched == str_p + 1) return str;

    str++;
  
  return NULL;

【讨论】:

我还没有尝试过你的解决方案,但非递归方法据说非常困难。 +1 尝试。【参考方案4】:

有效地做到这一点有问题吗?

简单的解决方案是循环遍历str 中每个大小为substr 的子字符串,从左到右,如果在比较中只有一个字符不同,则如果当前子字符串返回true。

令 n = str 的大小 设 m = substr 的大小

str中有O(n)子串,匹配步骤需要时间O(m)。 Ergo,天真的解决方案及时运行 O(n*m)

【讨论】:

我想,效率不会是他们在问题中测试的主要内容。我还是想说,让我们尝试在 O(n*m) 中完成。 -1 这种方法甚至不接近 IVlad 发布的实际解决方案。【参考方案5】:

带有任意编号。公差水平。

适用于我能想到的所有测试用例。大致基于 |/|ad 的解决方案。

#include<stdio.h>
#include<string.h>

report (int x, char* str, char* sstr, int[] t) 
    if ( x )
        printf( "%s is a substring of %s for a tolerance[%d]\n",sstr,str[i],t[i] );
    else
        printf ( "%s is NOT a substring of %s for a tolerance[%d]\n",sstr,str[i],t[i] );


int find_with_tolerance (char *str, char *sstr, int tol) 

    if ( (*sstr) == '\0' ) //end of substring, and match
        return 1;

    if ( (*str) == '\0' ) //end of string
        if ( tol >= strlen(sstr) ) //but tol saves the day
            return 1;
        else    //there's nothing even the poor tol can do
            return 0;

    if ( *sstr == *str )  //current char match, smooth
        return find_with_tolerance ( str+1, sstr+1, tol );
     else 
        if ( tol <= 0 ) //that's it. no more patience
            return 0;
        for(int i=1; i<=tol; i++) 
            if ( *(str+i) == *sstr ) //insertioan of a foreign character
                return find_with_tolerance ( str+i+1, sstr+1, tol-i );
            if ( *str == *(sstr+i) ) //deal with dletion
                return find_with_tolerance ( str+1, sstr+i+1, tol-i );
            if ( *(str+i) == *(sstr+i)  ) //deal with riplacement
                return find_with_tolerance ( str+i+1, sstr+i+1, tol-i );
            if ( *(sstr+i) == '\0' ) //substr ends, thanks to tol & this loop
                return 1;
        
        return 0; //when all fails
    


int find (char *str, char *sstr, int tol ) 
    int w = 0;
    while (*str!='\0')
        w |= find_with_tolerance ( str++, sstr, tol );
    return (w) ? 1 : 0;


int main() 
    const int n=3; //no of test cases
    char *sstr = "dog"; //the substr
    char *str[n] =  "doox", //those cases
                    "xxxxxd",
                    "xxdogxx" ;
    int t[] = 1,1,0; //tolerance levels for those cases
    for(int i = 0; i < n; i++) 
        report( find ( *(str+i), sstr, t[i] ), *(str+i), sstr, t[i] );
    
    return 0;

【讨论】:

以上是关于匹配字符串中的子字符串,允许 1 个字符不匹配的主要内容,如果未能解决你的问题,请参考以下文章

40 python 正则表达式 match方法匹配字符串 使用search函数在一个字符串中查找子字

将大字符串中的子字符串匹配到大量关键字的最佳方法是啥

在其他两个保守字符串之间提取字符串并允许 python 或 R 中的不匹配

mysql中的子字符串正则表达式匹配

JavaScript 字符串替换中的子匹配组引用是不是有分隔符/消歧语法?

错误太多参数试图匹配字符串bash中的子字符串[重复]