计算字符串出现次数和比较(KMP)的数量

时间:2016-01-10 18:56:56

标签: java pattern-matching

我尝试使用搜索算法 KMP 来计算模式发生次数和所需的比较(在下面的代码中称为匹配)。

我尝试过以下操作:

public class KMP {

    private String pat;
    private int[][] dfa;
    private static int match;
    private static int count;

    public KMP(String pat) {
        // Build DFA from pattern.      
        this.pat = pat;
        int M = pat.length();
        int R = 256;
        dfa = new int[R][M];
        dfa[pat.charAt(0)][0] = 1;
        for (int X = 0, j = 1; j < M; j++) {
            // Compute dfa[][j].         
            for (int c = 0; c < R; c++) {
                dfa[c][j] = dfa[c][X];           // Copy mismatch cases.        
                dfa[pat.charAt(j)][j] = j + 1;   // Set match case.    
                X = dfa[pat.charAt(j)][X];       // Update restart state.   
            }
        }
    }

    public int search(String txt) {
        // Simulate operation of DFA on txt.    
        int i, j, N = txt.length(), M = pat.length();
        for (i = 0, j = 0; i < N && j < M; i++) {
            j = dfa[txt.charAt(i)][j];
        }
        if (j == M) {
            return i - M; // found (hit end of pattern)  
        } else {
            return N;     // not found (hit end of text)  
        }
    }

    public static void main(String[] args) {

        String pat = "babba";
        String txt = "aaaaaaaaaaaabbaaababbaaaaababbaaaa";
        int lastIndex = 0;

        KMP kmp = new KMP(pat);
        int offset = kmp.search(txt);

        System.out.println("text:    " + txt);
        System.out.print("pattern: ");

        while (lastIndex != txt.length()) {
            for (int i = 0; i < offset; i++) {
                lastIndex++;
                match++;
            }
            count++;
        }

        System.out.println(pat);
        System.out.println("count:   " + count);
        System.out.println("match:   " + match);
    }
}

我的代码在编译时效果很好,但是当我将String txt属性更改为aaaaaaaaaaaabbaaababbaaaaababbaaaababba之类的内容时,它会给我一个意外的负数计数值(同样,它需要大约30实际运行代码的秒数。)

我试图找到一个更好的计算事件的解决方案,我也想知道我的代码有什么问题,因为它只适用于某些情况。

1 个答案:

答案 0 :(得分:1)

原因是你的循环条件。

while (lastIndex != txt.length())

您的问题字符串长度为38,偏移量为17 每个for循环lastIndex增加17 在第三个for循环之后,它具有值51 这符合条件,循环继续 它只在可能导致负计数值的几个int溢出之后结束。

你也无法计算那样的事件。
kmp.search()仅返回模式第一次出现的起始位置 例如

String txt = "aaaaaaaaaaaaaaaaababbaaaaaaaaaaaaa";

您的代码返回count = 2

解决方案是在每次搜索后拆分字符串,然后在模式后搜索子字符串。

KMP kmp = new KMP(pat);
int offset = kmp.search(txt);
while (offset != txt.length()) {
    count++;
    txt = txt.substring(offset+pat.length());
    offset = kmp.search(txt);
}
System.out.println("count:   " + count);

编辑:上面的代码仅适用于非重叠模式。

txt = txt.substring(offset+at.length());

需要更改为

txt = txt.substring(offset+1);

如果有重叠。