我有一组n个令牌(例如,a,b,c)分布在一堆其他令牌中。我想知道我的集合中的所有成员是否出现在给定数量的位置(窗口大小)内。在我看来,有可能编写一个RegEx来捕获这个状态,但确切的语法使我无法接受。
11111 012345678901234 ab ab bc a cba
在此示例中,给定窗口大小= 5,我想匹配位置12-14的cba
和位置3-7的abc
。
有没有办法用RegEx做到这一点,还是有其他类型的语法可以用来捕获这个逻辑?
我希望在Java中实现这一点。
答案 0 :(得分:2)
这是一个匹配包含所有'a','b'和'c'的5个字母序列的正则表达式:
(?=.{0,4}a)(?=.{0,4}b)(?=.{0,4}c).{5}
因此,虽然基本上匹配任意5个字符(.{5}
),但匹配必须遵守三个先决条件。它们中的每一个都需要一个令牌/字母(最多4个字符后跟'a'等)。 (?=X)
匹配“X,零宽度正向前瞻”,其中零宽度表示匹配时不移动字符位置。
使用正则表达式执行此操作很慢,但是..这是一个更直接的版本(似乎比使用正则表达式快15倍):
public static void find(String haystack, String tokens, int windowLen) {
char[] tokenChars = tokens.toCharArray();
int hayLen = haystack.length();
int pos = 0;
nextPos:
while (pos + windowLen <= hayLen) {
for (char c : tokenChars) {
int i = haystack.indexOf(c, pos);
if (i < 0) return;
if (i - pos >= windowLen) {
pos = i - windowLen + 1;
continue nextPos;
}
}
// match found at pos
System.out.println(pos + ".." + (pos + windowLen - 1) + ": " + haystack.substring(pos, pos + windowLen));
pos++;
}
}
答案 1 :(得分:2)
这个经过测试的Java程序有一个注释的正则表达式可以解决这个问题:
import java.util.regex.*;
public class TEST {
public static void main(String[] args) {
String s = "ab ab bc a cba";
Pattern p = Pattern.compile(
"# Match 5 char sequences containing: a and b and c\n" +
"(?=[abc]) # Assert first char is a, b or c.\n" +
"(?=.{0,4}a) # Assert an 'a' within 5 chars.\n" +
"(?=.{0,4}b) # Assert an 'b' within 5 chars.\n" +
"(?=.{0,4}c) # Assert an 'c' within 5 chars.\n" +
".{5} # If so, match the 5 chers.",
Pattern.COMMENTS);
Matcher m = p.matcher(s);
while (m.find()) {
System.out.print("Match = \""+ m.group() +"\"\n");
}
}
}
请注意,测试数据中有另一个有效序列S9:13" a cb"
(在S12:14"cba"
之前。假设您不想与此匹配,我添加了一个额外的约束来过滤它,这需要5个字符窗口必须以a
,b
或c
开头。
以下是脚本的输出:
Match = "ab bc"
Match = "a cba"
答案 2 :(得分:1)
嗯,一种可能性(尽管是完全不切实际的)只是与所有排列相匹配:
abc..|ab.c.|ab..c| .... etc.
这可以在某种程度上分解:
ab(c..|.c.|..c)|a.(bc.|b.c .... etc.
我不确定你是否可以用正则表达式做得更好。
答案 3 :(得分:0)
Pattern p = Pattern.compile("(?:a()|b()|c()|.){5}\\1\\2\\3");
String s = "ab ab bc a cba";
Matcher m = p.matcher(s);
while (m.find())
{
System.out.println(m.group());
}
输出:
ab bc
a cb
这受Regular Expressions Cookbook中食谱#5.7的启发。每个反向引用(\1
,\2
,\3
)的作用类似于零宽度断言,表示相应的捕获组参与了匹配,即使组本身没有消耗任何字符。
作者警告说,这种技巧依赖于大多数口味中没有记录的行为。它适用于Java,.NET,Perl,PHP,Python和Ruby(原始和Oniguruma),但不适用于JavaScript或ActionScript。