【问题标题】:Finding the shortest repetitive pattern in a string查找字符串中最短的重复模式
【发布时间】:2015-05-11 20:54:26
【问题描述】:

我想知道是否有办法在 Octave / matlab 中进行模式匹配?我知道 Maple 10 有执行此操作的命令,但不确定我需要在 Octave / Matlab 中做什么。所以如果一个数字是12341234123412341234,那么模式匹配就是1234。我试图找到重复生成整个字符串的最短模式

请注意:数字(仅使用数字)不会这么简单。另外,我不会提前知道模式(这就是我想要找到的)。请参阅下面的Maple 10 example,它表明该模式不是提前知道的,但该命令会找到该模式。

Maple 10 模式匹配示例:

ns:=convert(12341234123412341234,string);

             ns := "12341234123412341234"

StringTools:-PrimitiveRoot(ns);

             "1234"

如何在 Octave / Matlab 中做到这一点? Ps:我使用的是 Octave 3.8.1

【问题讨论】:

  • 没有人会去查找 Maple 示例。请在问题中提供相关信息。例如,您应该添加命令应该为您找到模式...
  • 模式的长度是否已知且固定?输入字符串是否仅包含模式或其他字符?
  • 我的问题是:字符串可以是1234912348123471234612345吗?想要找到的模式也可以是1234吗?
  • 嗯,还是不清楚。如果可能有多种模式,您想检测哪一种:最长的一种?出现次数最多的模式?那么最小长度是多少?想象一下1234123412341234123 比你找到的1234 四倍,但123 五倍,实际上1234123412341234123 也是一种模式。请提供一个更好的示例,涵盖所有特殊情况。
  • 但是 '12341234123412341234 ' is '12341234123412341234 ' 的最长模式可能是 '1234123412341234123 ' - 所以如果你陈述你的真实输入并不像您的示例,请发布您的真实输入:)

标签: regex matlab pattern-matching octave maple


【解决方案1】:

这个问题是你解决问题方法的一个很好的罗夏测验。我将添加一个信号工程解决方案,该解决方案应该很简单,因为信号应该是完全重复的,假设这成立:找到在重复时生成整个字符串的最短模式。

在下面的str 中,传入函数实际上是浮点数的列向量,而不是字符串,原始字符串已经用str2num(str2mat(str)') 转换:

function res=findshortestrepel(str);
[~,ii] = max(fft(str-mean(str)));
res = str(1:round(numel(str)/(ii-1)));

我进行了一个小测试,将其与regexp 解决方案进行比较,发现它总体上更快(蓝色方块),尽管有些不一致,并且前提是您不考虑将字符串转换为浮动向量(绿色方块)。但是我没有进一步追求这个(没有打破记录):

以秒为单位的时间。

【讨论】:

    【解决方案2】:

    我不确定这是否可以通过正则表达式来完成。这是一个脚本,可以在重复单词 pattern 的情况下执行您需要的操作。

    它遍历一个名为str 的字符串的字符,试图匹配另一个名为pattern 的字符串。如果匹配失败,pattern 字符串会根据需要进行扩展。

    编辑:我使代码更紧凑。

    str = 'lullabylullabylullaby';
    
    pattern = str(1);
    matchingState = false;
    sPtr = 1;
    pPtr = 1;
    
    while sPtr <= length(str)
         if str(sPtr) == pattern(pPtr) %// if match succeeds, keep looping through pattern string
                matchingState = true;
                pPtr = pPtr + 1;
                pPtr = mod(pPtr-1,length(pattern)) + 1;
         else                          %// if match fails, extend pattern string and start again
                if matchingState
                    sPtr = sPtr - 1;   %// don't change str index when transitioning out of matching state
                end  
                matchingState = false;
                pattern = str(1:sPtr);
                pPtr = 1;
         end
    
         sPtr = sPtr + 1;
    
    end
    
    display(pattern);
    

    输出是:

    pattern =
    
    lullaby
    

    注意:

    这不允许在 pattern 字符串的出现之间使用任意分隔符。比如str = 'lullaby1lullaby2lullaby1lullaby2';,那么

    pattern =
    
    lullaby1lullaby2
    

    这也允许pattern 在循环中途结束而不改变结果。例如,str = 'lullaby1lullaby2lullaby1'; 仍然会导致

    pattern =
    
    lullaby1lullaby2
    

    要解决这个问题,您可以添加行

    if pPtr ~= length(pattern)
        pattern = str;
    end
    

    【讨论】:

    • 你使用什么编程语言?
    • @Jan 这是 MATLAB,一种主要供工程师和科学家用于数值计算的语言。公认的解决方案使用大多数语言都支持的正则表达式要好得多。
    • 谢谢。很难找到关于这种语言的好文档。我正在尝试将其转换为 C#。 “pattern = str(1);”这行是什么?和“模式= str(1:sPtr);”和“if str(sPtr) == pattern(pPtr)”到底是什么?我将其转换为resp。 “字符串模式 = s.Substring(0,1);”, “模式 = s.Substring(0, sPtr);”和“如果(s.Substring(sPtr)== pattern.Substring(pPtr))”。我还注意到 Matlab 字符串在 C# 中是从 1 开始的,而不是从零开始的。
    • @Jan 看起来您已或多或少正确地转换了代码。如果您为此苦苦挣扎,我建议您提出一个新问题。同样,为了在文本中搜索模式,我强烈建议您学习正则表达式(C# 支持它们)并在接受的答案中使用正则表达式字符串之一。
    • 接受的使用正则表达式的答案并非在所有情况下都有效。我的第一个解决方案是使用正则表达式,但它在非常大的字符串上失败了。但是我现在正在运行您的代码。我现在正在尝试对其进行迭代,以便它支持像“1666666”这样的输入字符串,以便它返回 6。
    【解决方案3】:

    另一种做法如下:

    1. 确定字符串的长度,找出字符串长度值的所有可能因素
    2. 对于每个可能的因子长度,重塑字符串并检查 对于重复的子字符串

    要查找所有可能的因素,请参阅 SO 上的this 解决方案。下一步可以通过多种方式执行,但我在一个简单的循环中实现它,从最小的因子长度开始。

    function repeat = repeats_in_string(str);
    ns = numel(str);
    nf = find(rem(ns, 1:ns) == 0);
    for ii=1:numel(nf)
        repeat = str(1:nf(ii));
        if all(ismember(reshape(str,nf(ii),[])',repeat)); 
            break;
        end
    end 
    

    【讨论】:

      【解决方案4】:

      要找到在重复时生成整个字符串的最短模式,您可以使用正则表达式,如下所示:

      result = regexp(str, '^(.+?)(?=\1*$)', 'match');
      

      一些例子:

      >> str = '12341234123412341234';
      >> result = regexp(str, '^(.+?)(?=\1*$)', 'match')
      result = 
          '1234'
      
      >> str = '1234123412341234123';
      >> result = regexp(str, '^(.+?)(?=\1*$)', 'match')
      result = 
          '1234123412341234123'
      
      >> str = 'lullabylullaby';
      >> result = regexp(str, '^(.+?)(?=\1*$)', 'match')
      result = 
          'lullaby'
      
      >> str = 'lullaby1lullaby2lullaby1lullaby2';
      >> result = regexp(str, '^(.+?)(?=\1*$)', 'match')
      result = 
          'lullaby1lullaby2'
      

      【讨论】:

      • @eigenchris 我也是 :-) 我对regexp 的所有回答都应该归功于this page!顺便说一句,我借用了你的例子
      • 为什么str = '12341235123412351234';的结果不是 '1234?关于我的一个 cmets 它应该。我真的不明白这个问题。我真的很努力。
      • 我认为,虽然 OP 的问题陈述模棱两可,但这可能是他们正在寻找的答案。它符合等效 Maple 命令PrimativeRoot() 的规范。
      • @LuisMendo 我们真的应该介绍精神学家奖;)
      • OP 再次冲击我们的心灵,让我们感到困惑......从所有这些混乱的灰烬中诞生了一个心智家,其唯一目的是消除带给这个世界的混乱.其他几个人是从这个事件中产生的,从而创造了……心灵学家联盟。去心理医生去!
      猜你喜欢
      • 2014-03-24
      • 1970-01-01
      • 2015-01-27
      • 1970-01-01
      • 1970-01-01
      • 2012-11-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多