3-14 正则表达式匹配问题
问题描述
许多操作系统采用正则表达式实现文件匹配功能。一种简单的正则表达式由英文字母、数字及通配符“ * ”和“?”组成。“?”代表任意一个字符。“ * ”则可以代表任意多个字符。 现要用正则表达式对部分文件进行操作。
试设计一个算法,找出一个正则表达式,使其能匹配的待操作文件最多,但不能匹配任何不进行操作的文件。所找出的正则表达式的长度还应是最短的。
数据输入:
输入由 n(1≤n≤250)行组成。每行给出一个文件名。文件名由英文字母和数字组成。英文字符要区分大小写,文件名长度不超过 8 个字符。 文件名后是一个空格符和一个字符“+”或“-”。“+”表示要对该行给出的文件进行操作, “-”表示不进行操作。
Java
import java.util.Scanner;
class Cha{
char c; //字符
int f; //字符出现的频率
}
public class ZhengZeBiaoDaShiPiPei {
/*
正则表达式可选字符集的排列顺序先为‘*’,‘?’,操作文件名序列中出现的所有字符按其频率递减的次序随后
大体思路:
正则表达式为s,当前考察文件为f
match(i, j)为s[1,i]与f[1,j]匹配情况
如果match(i-1, j-1) = 1, s[i] = '?'
match(i-1, j-1) = 1, s[i] = f[j]
match(i-1, k) = 1, s[i] = '*'
match(i, j) = 1
否则 match(i, j) = 0
*/
private static int MAXN = 250; //文件数
// private static int MAXL = 8; //文件名长度
private static int MAXP = 62; //大写字母 + 小写字母 + 数字
private static int minlen; //最优正则表达式长度
private static int maxmat; //最优正则表达式所能匹配的操作文件数
private static int curmat; //当前正则式所能匹配的操作文件数
private static String[] f = new String[MAXN+1]; //操作文件 + 非操作文件
private static String[] k = new String[MAXN+1]; //非操作文件
private static int[] n = new int[2]; //n[0]为操作文件数,n[1]为非操作文件数
private static int[] p = new int[MAXP]; //p[len-1] 存储 s[len]可选字符数
private static char[] s = new char[MAXP]; //存储临时正则表达式
private static char[] minmat = new char[MAXP]; //最优正则表达式
private static int[][][] match = new int[MAXP][MAXN+1][MAXP]; //match[len][i][j]表示正则表达式的第len个字符与第i个文件的第j个字符的匹配情况
private static Cha[][] cha = new Cha[MAXN][MAXP]; //cha[len][i]表示正则表达式长度为len时的字符排序情况
public static void main(String[] args){
readin();
search(0);
out();
}
private static void readin(){
Scanner input = new Scanner(System.in);
n[0] = 0;
n[1] = 0;
p[0] = 0;
String str;
String ch;
for(int i=0; i<MAXN; i++)
for(int j=0; j<MAXP; j++)
cha[i][j] = new Cha();
while(input.hasNext()){
str = input.next();
ch = input.next();
if(ch.equals("+")){
f[++n[0]] = str;
save(f[n[0]].charAt(0), 0);
}
else{
k[++n[1]] = str;
}
}
for(int i=1; i<=n[1]; i++)
f[n[0]+i] = k[i];
for(int i=1; i<=n[0]+n[1]; i++)
match[0][i][0] = 1;
maxmat = 0;
minlen = 255;
}
//对操作文件名中出现的字符按出现频率排序存储,以加快搜索进程
private static void save(char c, int len){
int i, j;
for(i=1; i<=p[len]; i++)
if(cha[len][i].c == c){
cha[len][i].f++;
cha[len][0] = cha[len][i];
j = i;
while(cha[len][j-1].f < cha[len][0].f){ //将频率小于当前字符频率的字母后移
cha[len][j] = cha[len][j-1];
j--;
}
cha[len][j] = cha[len][0]; //将当前字母放到适当位置
return;
}
//如果p[len] = 0,或者字符c第一次出现,则增加一个字符
cha[len][++p[len]].c = c;
cha[len][p[len]].f = 1;
}
//计算当前匹配情况
private static boolean check(int len){
int i, j, t, k = 0;
curmat = 0;
for(i=1; i<=n[0]; i++){ //操作文件数
for(j=0; j<MAXP; j++)
match[len][i][j] = 0;
if(len==1 && s[1]=='*')
match[len][i][0] = 1;
for(j=1; j<=f[i].length(); j++){ //操作文件的每个字符
switch(s[len]){
case '*':
for(t=0; t<=j; t++)
if(match[len-1][i][t]==1){
match[len][i][j] = 1; //正则表达式的第len个字符与第i个文件的第j个字符的匹配情况
break;
}
break;
case '?':
match[len][i][j] = match[len-1][i][j-1];
break;
default:
if(s[len]==f[i].charAt(j-1))
match[len][i][j] = match[len-1][i][j-1];
break;
}
}
for(j=f[i].length(); j>=1; j--){
if(match[len][i][j] == 1){
k++;
if(j == f[i].length()) //第i个文件与正则表达式匹配
curmat++;
break;
}
}
}
if(k<maxmat || k==maxmat && len>=minlen)
return false;
p[len] = 0;
for(i=1; i<=n[0]; i++) //对与正则表达式匹配的文件中的字符重新排序,以便正则式下次扩展
for(j=1; j<f[i].length(); j++)
if(match[len][i][j]==1)
save(f[i].charAt(j), len);
return true;
}
//判断是否匹配非操作文件
private static boolean ok(int len){
int i, j, l, t;
for(l=1; l<=len; l++){
for(i=n[0]+1; i<=n[0]+n[1]; i++){
for(j=0; j<MAXP; j++)
match[l][i][j] = 0;
if(s[1]=='*' && l==1)
match[l][i][0] = 1;
for(j=1; j<=f[i].length(); j++){
switch(s[l]){
case '*':
for(t=0; t<=j; t++)
if(match[l-1][i][t]==1){
match[l][i][j] = 1;
break;
}
break;
case '?':
match[l][i][j] = match[l-1][i][j-1];
break;
default:
if(s[l]==f[i].charAt(j-1))
match[l][i][j] = match[l-1][i][j-1];
break;
}
}
}
}
for(i=n[0]+1; i<=n[0]+n[1]; i++) //如果正则表达式与非操作文件匹配
if(match[len][i][f[i].length()]==1)
return false;
return true;
}
//求最优匹配的回溯法
//len 为当前正则表达式长度
//正则表达式可选字符集的排列顺序先为‘*’,‘?’,操作文件名序列中出现的所有字符按其频率递减的次序随后
private static void search(int len){
if((curmat>maxmat || (curmat==maxmat && len<minlen)) && ok(len)){
maxmat = curmat;
minlen = len;
for(int i=0; i<=minlen; i++)
minmat[i] = s[i];
}
len++;
if(len==1 || s[len-1]!='*'){
s[len] = '?';
if(check(len))
search(len);
s[len] = '*';
if(check(len))
search(len);
}
for(int i=1; i<=p[len-1]; i++){
s[len] = cha[len-1][i].c;
if(check(len))
search(len);
}
}
private static void out(){
System.out.println(maxmat);
for(int i=1; i<=minlen; i++)
System.out.print(minmat[i]);
}
}
Input & Output
EXCHANGE +
EXTRA +
HARDWARE +
MOUSE -
NETWORK -
^D
3
*A*
Reference
王晓东《计算机算法设计与分析》(第3版)P93-94
https://blog.youkuaiyun.com/u012319493/article/details/49976897

该博客讨论了如何设计一个算法来解决正则表达式匹配问题,旨在找到能匹配最多‘+’标记的文件且不匹配任何‘-’标记的文件的最短正则表达式。输入包括文件名列表,每个文件名后跟随操作标志‘+’或‘-’。解决方案可能涉及动态规划,并参考了王晓东的《计算机算法设计与分析》。
2592

被折叠的 条评论
为什么被折叠?



