【问题标题】:Search for pattern in a string在字符串中搜索模式
【发布时间】:2012-11-01 05:12:26
【问题描述】:

字符串中的模式搜索。

例如。

$string = "111111110000";
FindOut($string);

函数应该返回 0

function FindOut($str){    
    $items =  str_split($str, 3);    
    print_r($items);
}

【问题讨论】:

  • 那么问题是什么?
  • 此外,如果您包含一些示例代码以及您尝试解决问题的方式,即使该代码不起作用,我们也更容易回答您的问题。
  • 我试图将字符串分解为每个包含 3 个字符的数组并将它们全部匹配。但没有返回。
  • 你是如何将字符串分解成数组的?您可以编辑您的答案以向我们展示此代码吗?
  • 110101011怎么样?我猜结果应该是 1 因为 101 发生了两次。但如果你拆分 3 个字符的数组,你不会发现:110 101 011。

标签: php algorithm search pattern-matching


【解决方案1】:

如果我理解正确,您的问题归结为找出 3 个字符的子字符串是否在字符串中出现两次而不重叠。如果确实如此,这将为您提供第一次出现的位置:

function findPattern($string, $minlen=3) {
    $max = strlen($string)-$minlen;
    for($i=0;$i<=$max;$i++) {
        $pattern = substr($string,$i,$minlen);
        if(substr_count($string,$pattern)>1)
            return $i;
    }
    return false;
}

或者我在这里遗漏了什么?

【讨论】:

    【解决方案2】:

    从概念上讲,您可以使用滑动窗口来解决这里的问题。对于您的示例,您有一个大小为 3 的滑动窗口。

    对于字符串中的每个字符,您将当前字符的子字符串和接下来的两个字符作为当前模式。然后将窗口向上滑动一个位置,并检查字符串的其余部分是否包含当前模式包含的内容。如果是,则返回当前索引。如果没有,请重复。

    例子:

    1010101101
    |-|
    

    所以,模式 = 101。现在,我们将滑动窗口前进一个字符:

    1010101101
     |-|
    

    看看字符串的其余部分是否有101,检查每个3个字符的组合。

    从概念上讲,这应该是解决此问题所需的全部内容。

    编辑:我真的不喜欢人们只要求代码,但由于这似乎是一个有趣的问题,这是我对上述算法的实现,它允许窗口大小变化(而不是固定在 3,该函数只进行了简单的测试并省略了明显的错误检查):

    function findPattern( $str, $window_size = 3) {
        // Start the index at 0 (beginning of the string)
        $i = 0;
    
        // while( (the current pattern in the window) is not empty / false)
        while( ($current_pattern = substr( $str, $i, $window_size)) != false) {
            $possible_matches = array();
    
            // Get the combination of all possible matches from the remainder of the string
            for( $j = 0; $j < $window_size; $j++) {
                $possible_matches = array_merge( $possible_matches, str_split( substr( $str, $i + 1 + $j), $window_size));
            }
    
            // If the current pattern is in the possible matches, we found a duplicate, return the index of the first occurrence
            if( in_array( $current_pattern, $possible_matches)) {
                return $i;
            }
    
            // Otherwise, increment $i and grab a new window
            $i++;
        }
        // No duplicates were found, return -1
        return -1;
    }
    

    应该注意,这当然不是最有效的算法或实现,但它应该有助于澄清问题并给出如何解决问题的简单示例。

    【讨论】:

      【解决方案3】:

      看起来您更想使用子字符串函数来遍历并检查每三个字符,而不仅仅是将其分成 3 个

      function fp($s, $len = 3){
        $max = strlen($s) - $len; //borrowed from lafor as it was a terrible oversight by me
        $parts = array();
      
        for($i=0; $i < $max; $i++){
          $three = substr($s, $i, $len);
          if(array_key_exists("$three",$parts)){
                return $parts["$three"];  
          //if we've already seen it before then this is the first duplicate, we can return it
          }
          else{
            $parts["$three"] = i; //save the index of the starting position.
          }
        }
      
        return false; //if we get this far then we didn't find any duplicate strings
      }
      

      【讨论】:

      • 这会以一种有效的方式找到匹配项,但不会告诉您是否找到了匹配项。您能否再次扫描该字符串并找到第一个 length=3 且在 $parts 中的计数大于 1 的字符串?
      • 您是否在寻找具有重复值的前 3 个字符?不是重复次数最多的?
      • 我不是 OP,但 OP 似乎确实在根据问题的previous version 寻找第一次出现。
      • @lafor 有您想要的解决方案
      • 他有,但你的运行时间是线性的,他的运行时间是二次的。所以我更喜欢你的。 :-)
      【解决方案4】:

      基于str_split documentation,在"1010101101" 上调用str_split 将导致:

      Array(
        [0] => 101
        [1] => 010
        [2] => 110
        [3] => 1
      }
      

      这些都不会相互匹配。

      您需要查看字符串的每个 3 长片段(从索引 0 开始,然后是索引 1,依此类推)。

      我建议查看substr,您可以这样使用:

      substr($input_string, $index, $length)
      

      它将为您提供$input_string 的部分,从$index 开始,长度为$length

      【讨论】:

        【解决方案5】:

        这种模式搜索的快速而肮脏的实现:

        function findPattern($string){
            $matches = 0;
            $substrStart = 0;
        
            while($matches < 2 && $substrStart+ 3 < strlen($string) && $pattern = substr($string, $substrStart++, 3)){
                $matches = substr_count($string,$pattern);
            }
        
            if($matches < 2){
                return null;
            }
            return $substrStart-1;
        

        【讨论】:

          猜你喜欢
          • 2012-08-30
          • 2022-01-23
          • 2021-11-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-31
          相关资源
          最近更新 更多