8

同じ文字(または理想的には同じグループ)のN回の繰り返しに正確に一致する式を作成するにはどうすればよいですか?基本的には何(.)\1{N-1}をしますが、1つの重要な制限があります。つまり、サブジェクトがN回以上繰り返されると、式は失敗するはずです。たとえば、与えられN=4た文字列xxaaaayyybbbbbzzccccxxの場合、式は一致する必要がaaaaあり、一致する必要がccccありbbbbます。

私は特定の方言に焦点を合わせていません。どんな言語でも自由に使ってください。この特定の例でのみ機能するコードを投稿しないでください。一般的な解決策を探しています。

4

8 に答える 8

12

ネガティブルックアヘッドネガティブルックビハインドを使用します。

これは正規表現になります:(.)(?<!\1.)\1{N-1}(?!\1)Pythonのreモジュールが壊れていることを除いて(このリンクを参照)。

英語訳:「任意の文字に一致します。その文字に一致した後、その前の文字がその文字ではないことを確認します。その文字のN-1回の繰り返しに一致します。それらの繰り返しの後の文字もそれではないことを確認します。キャラクター。"

残念ながら、reモジュール(およびほとんどの正規表現エンジン)は壊れており、後読みアサーションで後方参照を使用することはできません。後読みアサーションは一定の長さである必要があり、コンパイラーは、後方参照が使用されている場合を推測するほど賢くありません(この場合のように、後方参照は一定の長さですが)。次のように、これを介して正規表現コンパイラを保持する必要があります。

実際の答えはもっと厄介でなければなりません:r"(.)(?<!(?=\1)..)\1{N-1}(?!\1)"

(?=\1)..これは、代わりにを使用することでreモジュールのバグを回避します\1.(これらはほとんどの場合同等です)。これにより、正規表現エンジンは後読みアサーションの幅を正確に認識できるため、PCREなどで機能します。


もちろん、実際のソリューションは次のようなものです。[x.group() for x in re.finditer(r"(.)\1*", "xxaaaayyybbbbbzzccccxx") if len(x.group()) == 4]

于 2012-04-25T16:57:59.137 に答える
6

ネガティブ先読みを使用したいと思います: (.)\1{N-1}(?!\1)

しかし、そうは言っても...最も単純なクロスランゲージソリューションは、正規表現を使用せずに自分で作成することだと思います。

アップデート:

^(.)\\1{3}(?!\\1)|(.)(?<!(?=\\2)..)\\2{3}(?!\\2)文字列の先頭から始まる一致を含め、より一般的に機能します。

于 2012-04-25T16:21:04.143 に答える
2

ほぼすべてが実行されるときに、正規表現に過度の負担をかけ、すべてを実行させようとするのは簡単です。

正規表現を使用して、単一の文字で構成されるすべてのサブ文字列を検索し、次のようにそれらの長さを個別に確認します。

use strict;
use warnings;

my $str = 'xxaaaayyybbbbbzzccccxx';

while ( $str =~ /((.)\2*)/g ) {
  next unless length $1 == 4;
  my $substr = $1;
  print "$substr\n";
}

出力

aaaa
cccc
于 2012-04-25T17:02:12.030 に答える
2

Perlの正規表現エンジンは可変長のルックビハインドをサポートしていないため、慎重に検討する必要があります。

sub runs_of_length {
  my($n,$str) = @_;

  my $n_minus_1 = $n - 1;
  my $_run_pattern = qr/
    (?:
       # In the middle of the string, we have to force the
       # run being matched to start on a new character.
       # Otherwise, the regex engine will give a false positive
       # by starting in the middle of a run.
       (.) ((?!\1).) (\2{$n_minus_1}) (?!\2) |
       #$1 $2        $3

       # Don't forget about a potential run that starts at
       # the front of the target string.
           ^(.)      (\4{$n_minus_1}) (?!\4)
       #    $4       $5
    )
  /x;

  my @runs;
  while ($str =~ /$_run_pattern/g) {
    push @runs, defined $4 ? "$4$5" : "$2$3";
  }

  @runs;
}

いくつかのテストケース:

my @tests = (
  "xxaaaayyybbbbbzzccccxx",
    "aaaayyybbbbbzzccccxx",
  "xxaaaa",
    "aaaa",
  "",
);

$" = "][";
for (@tests) {
  my @runs = runs_of_length 4, $_;
  print qq<"$_":\n>,
        "  - [@runs]\n";
}

出力:

"xxaaaayyybbbbbzzccccxx":
  -[aaaa] [cccc]
"aaaayyybbbbbzzccccxx":
  -[aaaa] [cccc]
「xxaaaa」:
  -[aaaa]
「aaaa」:
  -[aaaa]
"":
  -[]

これは楽しいパズルですが、正規表現を嫌う同僚は、そのような構造が本番コードに表示された場合、不幸になる可能性があります。

于 2012-04-25T17:35:07.377 に答える
1
>>> import itertools
>>> zz = 'xxaaaayyybbbbbzzccccxxaa'
>>> z = [''.join(grp) for key, grp in itertools.groupby(zz)]  
>>> z  
['xx', 'aaaa', 'yyy', 'bbbbb', 'zz', 'cccc', 'xx', 'aa']

そこから、リストを繰り返し処理して、次のN==4ように非常に簡単な場合を確認できます。

>>> [item for item in z if len(item)==4]
['cccc', 'aaaa']
于 2012-04-25T16:38:31.897 に答える
1

Pythonではこれはどうですか?

def match(string, n):
    parts = []
    current = None
    for c in string:
        if not current:
            current = c
        else:
            if c == current[-1]:
                current += c
            else:
                parts.append(current)
                current = c

    result = []
    for part in parts:
        if len(part) == n:
            result.append(part)

    return result

さまざまなサイズの文字列を使用したテスト:

match("xxaaaayyybbbbbzzccccxx", 6) = []
match("xxaaaayyybbbbbzzccccxx", 5) = ["bbbbb"]
match("xxaaaayyybbbbbzzccccxx", 4) = ['aaaa', 'cccc']
match("xxaaaayyybbbbbzzccccxx", 3) = ["yyy"]
match("xxaaaayyybbbbbzzccccxx", 2) = ['xx', 'zz']

説明:

最初のループは基本的に、テキストを次のように部分に分割します:["xx"、 "aaaa"、 "yyy"、 "bbbbb"、 "zz"、 "cccc"、"xx"]。次に、2番目のループでそれらのパーツの長さをテストします。最終的に、関数は現在の長さのパーツのみを返します。私はコードを説明するのが得意ではないので、必要に応じて誰でもこの説明を自由に拡張できます。

とにかく、これでいいと思います!

于 2012-04-25T16:55:26.800 に答える
1

正規表現エンジンに任せて、同じ記号の最長の文字列を見つけて、自分で長さを確認してみませんか?

Perlの場合:

my $str = 'xxaaaayyybbbbbzzccccxx';

while($str =~ /(.)\1{3,}/g){
    if(($+[0] - $-[0]) == 4){ # insert here full match length counting specific to language
        print (($1 x 4), "\n")
    }
}
于 2012-04-25T16:59:50.997 に答える
1

Javaでは、以下のコードのように行うことができます

String test ="xxaaaayyybbbbbzzccccxx  uuuuuutttttttt";

int trimLegth = 4; // length of the same characters

Pattern p = Pattern.compile("(\\w)\\1+",Pattern.CASE_INSENSITIVE| Pattern.MULTILINE);

Matcher m = p.matcher(test);
while (m.find())
{ 
    if(m.group().length()==trimLegth) {
        System.out.println("Same Characters String " + m.group());
    }
}
于 2012-04-26T01:26:36.267 に答える