文字列内の開始位置から最初に出現するパターンを見つけるにはどうすればよいでしょうか?

StackOverflow https://stackoverflow.com/questions/120071

  •  02-07-2019
  •  | 
  •  

質問

任意の長さの文字列があり、位置 p0 から開始して、3 つの 3 文字パターンのうちの 1 つが最初に出現するものを見つける必要があります。

文字列には文字のみが含まれていると仮定します。位置 p0 から開始して、「aaa」、「bbb」、または「ccc」のいずれかが最初に出現するまで、3 つずつ前方にジャンプする 3 つ組の数を見つける必要があります。

正規表現だけを使用してもこれは可能ですか?

役に立ちましたか?

解決

モーリッツ氏は、これは正規表現よりも高速である可能性があると述べています。多少遅くても午前5時の方がわかりやすいです。:)

             #0123456789.123456789.123456789.  
my $string = "alsdhfaaasccclaaaagalkfgblkgbklfs";  
my $pos    = 9;  
my $length = 3;  
my $regex  = qr/^(aaa|bbb|ccc)/;

while( $pos < length $string )    
    {  
    print "Checking $pos\n";  

    if( substr( $string, $pos, $length ) =~ /$regex/ )
        {
        print "Found $1 at $pos\n";
        last;
        }

    $pos += $length;
    }

他のヒント

$string=~/^   # from the start of the string
            (?:.{$p0}) # skip (don't capture) "$p0" occurrences of any character
            (?:...)*?  # skip 3 characters at a time,
                       # as few times as possible (non-greedy)
            (aaa|bbb|ccc) # capture aaa or bbb or ccc as $1
         /x;

(p0 が 0 ベースであると仮定します)。

もちろん、文字列に対して substr を使用して前方にスキップする方がおそらくより効率的です。

substr($string, $p0)=~/^(?:...)*?(aaa|bbb|ccc)/;

正規表現を使って実際にカウントすることはできませんが、次のようなことはできます。

pos $string = $start_from;
$string =~ m/\G         # anchor to previous pos()
            ((?:...)*?) # capture everything up to the match
            (aaa|bbb|ccc)
            /xs  or die "No match"
my $result = length($1) / 3;

しかし、substr() と unpack() を使用してトリプルに分割し、そのトリプルを for ループ内でたどるほうがもう少し速いと思います。

(編集:それは length() ではなく length() です ;-)

この主要部分は分割 /(...)/ です。ただし、これが完了すると、位置と発生データが得られます。

my @expected_triplets = qw<aaa bbb ccc>;
my $data_string      
    = 'fjeidoaaaivtrxxcccfznaaauitbbbfzjasdjfncccftjtjqznnjgjaaajeitjgbbblafjan'
    ;
my $place          = 0;
my @triplets       = grep { length } split /(...)/, $data_string;
my %occurrence_for = map { $_, [] } @expected_triplets;
foreach my $i ( 0..@triplets ) {
    my $triplet = $triplets[$i];
    push( @{$occurrence_for{$triplet}}, $i ) if exists $occurrence_for{$triplet};
}

または、正規表現による単純なカウントの場合 (実験的 (??{}) を使用します)。

my ( $count, %count );
my $data_string      
    = 'fjeidoaaaivtrxxcccfznaaauitbbbfzjasdjfncccftjtjqznnjgjaaajeitjgbbblafjan'
    ;
$data_string =~ m/(aaa|bbb|ccc)(??{ $count++; $count{$^N}++ })/g;

速度を重視する場合は、3 つの文字列の内容に応じて、ツリーを作成して非常に豪華にすることができます (例:Aho-Corasick アルゴリズムなど)。

あらゆる可能な状態のマップが可能です。「a」で始まる文字列がない場合は、state[0]['a'] = 0。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top