算法设计与分析: 3-14 正则表达式匹配问题

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; ifor(int j=0; jnew 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; j0;
            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=minlen)
            return false;
        p[len] = 0;
        for(i=1; i<=n[0]; i++) //对与正则表达式匹配的文件中的字符重新排序,以便正则式下次扩展
            for(j=1; jif(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; j0;
                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 && lenfor(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.csdn.net/u012319493/article/details/49976897

你可能感兴趣的:(Algorithm,动态规划,计算机算法设计与分析,Java,计算机算法设计与分析)