How would you match a^n b^n c^n for n > 0 with PCRE?
The following cases should match:
abc
aabbcc
aaabbbccc
The following cases should not match:
abbc
aabbc
aabbbccc
Here's what I've "tried"; /^(a(?1)?b)$/gmx
but this matches a^n b^n for n > 0:
ab
aabb
aaabbb
Note: This question is the same as this one with the change in language.
Answer
Qtax trick
(The mighty self-referencing capturing group)
^(?:a(?=a*(\1?+b)b*(\2?+c)))+\1\2$
This solution is also named "The Qtax trick" because it uses the same technique as from "vertical" regex matching in an ASCII "image" by Qtax.
The problem in question burns down to a need to assert that three groups are matched of the same length. As a simplified version, to match:
xyz
Where x
, y
and z
are really just subpatterns with a variable with matching length n
of a
, b
and c
. With an expression that uses lookaheads with self-referencing capturing groups, a character we specify is added to each repetition of the lookahead, which can effectively be used to "count":
aaabbbccc
^ ^ ^
This is achieved by the following:
(?:a…)+
A character of subpatterna
is matched. With(?=a*
, we skip directly to the "counter".(\1?+b)
Capturing group (\1
) effectively consumes whatever has previously been matched, if it is there, and uses a possessive match which does not permit backtracking, and the match fails if the counter goes out of sync - That is, there has been more of subpatternsb
than subpatterna
. On the first iteration, this is absent, and nothing is matched. Then, a character of subpatternb
is matched. It is added to the capturing group, effectively "counting" one ofb
in the group. Withb*
, we skip directly to the next "counter".(\2?+c)
Capturing group (\2
) effectively consumes whatever has previously been matched just like the above. Because this additional character capture works just like the previous group, characters are allowed to sync up in length within these character groups. Assuming continuous sequences ofa..b..c..
:
(Excuse my art.)
First iteration:
| The first 'a' is matched by the 'a' in '^(?:a…)'.
| The pointer is stuck after it as we begin the lookahead.
v,- Matcher pointer
aaaa...bbbbbbbb...cccc...
^^^ |^^^ ^
skipped| skipped Matched by c in (\2?+c);
by a* | by b* \2 was "nothing",
| now it is "c".
Matched by b
in (\1?+b).
\1 was "nothing", now it is "b".
Second iteration:
| The second 'a' is matched by the 'a' in '^(?:a…)'.
| The pointer is stuck after it as we begin the lookahead.
v,- Matcher pointer
aaaa...bbbbbbbb...cccc...
/|^^^ |^
eaten by| skipped |Matched by c in (\2?+c);
\1?+ | by b* | '\2' was "nothing",
^^ | \2?+ now it is "cc".
skipped|
by a* \ Matched by b
in (\1?+b).
'\1' was "nothing", now it is "bb".
As the three groups discussed above "consumes" one of each of a
, b
, c
respectively, they are matched in round-robin style and "counted" by the (?:a…)+
, (\1?+b)
and (\2?+c)
groups respectively. With the additional anchoring and capturing what we started, we can assert that we match xyz
(Representing each group above) where x
, y
and z
are an
, bn
and cn
respectively.
As a bonus, to "count" more, one can do this:
Pattern: ^(?:a(?=a*(\1?+b)b*(\2?+c)))+\1{3}\2$
Matches: abbbc
aabbbbbbcc
aaabbbbbbbbbccc
Pattern: ^(?:a(?=a*(\1?+bbb)b*(\2?+c)))+\1\2$
Matches: abbbc
aabbbbbbcc
aaabbbbbbbbbccc
No comments:
Post a Comment