▶ 书中第五章部分程序,包括在加上自己补充的代码,非确定性有穷自动机(NFA),grep 命令(利用 NFA 匹配)

● 非确定性有穷自动机(NFA)

 package package01;

 import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.Bag;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.DirectedDFS; public class class01
{ private Digraph graph; // 含 ε-转移 的有穷自动机图
private String regexp; // 输入的正则表达式
private final int m; // 正则表达式包含的字符数 public class01(String inputRegexp) // 根据正则表达式构造 NFA
{
regexp = inputRegexp;
m = regexp.length();
Stack<Integer> ops = new Stack<Integer>();
graph = new Digraph(m + 1); // 自动机的状态数比正则表达式多 1
for (int i = 0; i < m; i++)
{
int lp = i; // 指向当前操作数
if (regexp.charAt(i) == '(' || regexp.charAt(i) == '|') // 遇到 '(' 和 '|',压栈
ops.push(i);
else if (regexp.charAt(i) == ')') // 遇到 ')',吐栈
{
int or = ops.pop();
if (regexp.charAt(or ) == '|') // 吐出'|',需要添加两条边,设原文为 (A|B)
{
lp = ops.pop(); // 取出在此之前的 '('
graph.addEdge(lp, or +1); // 第一条边从 '(' 指向 '|' 的后一节点,表示支路 B
graph.addEdge(or , i); // 第二条边从 '|' 指向 ')' 之后,表示支路 A
}
else if (regexp.charAt(or ) == '(') // 吐出 '(',说明存在一个整体'(...)',用 lp 标记起点,服务于后边的 '*'
lp = or ;
else
assert false; // 栈顶是其他东西(遇不到该分支?因为没有把其他东西压入栈中)
}
if (i < m - 1 && regexp.charAt(i + 1) == '*') // 下一个字符是闭包,需要添加两条边
{
graph.addEdge(lp, i + 1); // 第一条边从当前节点指向 '*' 节点
graph.addEdge(i + 1, lp); // 第二条边从 '*' 节点指向当前节点,如果当前节点是 ')',则指向当前整体的起点处
}
if (regexp.charAt(i) == '(' || regexp.charAt(i) == '*' || regexp.charAt(i) == ')') // 如果当前符号是'(*)' 三者之一,则添加添加一条正常边指向下一节点
graph.addEdge(i, i + 1);
}
if (ops.size() != 0)
throw new IllegalArgumentException("Invalid regular expression");
} public boolean recognizes(String txt) // 使用生成的 NFA 识别输入的字符串
{
Bag<Integer> pc = new Bag<Integer>(); // 存放当前能够到达的所有节点
DirectedDFS dfs = new DirectedDFS(graph, 0); // 从节点 0 深度优先遍历,表示读取正文第 0 位之前就能通过 ε-转移 到达的所有状态,放入背包 pc 中
for (int v = 0; v < graph.V(); v++)
{
if (dfs.marked(v))
pc.add(v);
}
for (int i = 0; i < txt.length(); i++) // 循环每次取原文的一个字符
{
if (txt.charAt(i) == '*' || txt.charAt(i) == '|' || txt.charAt(i) == '(' || txt.charAt(i) == ')') // 被匹配的原文不能包含 '(*|)'
throw new IllegalArgumentException("text contains the metacharacter '" + txt.charAt(i) + "'");
Bag<Integer> match = new Bag<Integer>(); // 临时背包,用于存放能与 txt[i] 匹配的所有正则表达式的状态(“状态” 指的是节点编号)
for (int v : pc) // 遍历 pc,即以当前能够到达的所有状态为起点尝试匹配 txt[i]
{
if (v == m) // pc 包含节点 m,说明已经到达了终点,完成匹配
continue;
if ((regexp.charAt(v) == txt.charAt(i)) || regexp.charAt(v) == '.') // txt[i] 与正则表达式当前的某个状态可以匹配,向 match 中写入 v 的下一节点
match.add(v + 1);
}
dfs = new DirectedDFS(graph, match); // 以 match 所有元素为起点深度优先遍历,表示当前所有可达节点通过 ε-转移 到达的所有状态
pc = new Bag<Integer>(); // 更新 pc
for (int v = 0; v < graph.V(); v++)
{
if (dfs.marked(v))
pc.add(v);
}
if (pc.size() == 0) // pc 空,说明没有任何可达状态了,停止匹配
return false;
}
for (int v : pc) // 遍历 pc,如果包含节点 m,说明到达了终点,完成匹配
{
if (v == m)
return true;
}
return false;
} public static void main(String[] args)
{
String regexp = "(" + args[0] + ")", txt = args[1]; // 输入的正则表达式最外层用括号包住
class01 nfa = new class01(regexp);
StdOut.println(nfa.recognizes(txt));
}
}

● grep 命令实现

 package package01;

 import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.NFA; public class class01
{
private class01() {} public static void main(String[] args)
{
String regexp = "(.*" + args[0] + ".*)";
for (NFA nfa = new NFA(regexp); StdIn.hasNextLine();)
{
String line = StdIn.readLine();
if (nfa.recognizes(line))
StdOut.println(line);
}
}
}

《算法》第五章部分程序 part 6的更多相关文章

  1. 《算法》第五章部分程序 part 3

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(美国国旗排序) ● 美国国旗排序 package package01; import edu.princeton.cs.algs4 ...

  2. 《算法》第五章部分程序 part 8

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,适用于基因序列的 2-Bit 压缩算法,行程长压缩算法,Huffman 压缩算法,LZW 压缩算法 ● 适用于基因序列的 2-Bit 压缩算法 pac ...

  3. 《算法》第五章部分程序 part 7

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串的二进制表示.十六进制表示.图形表示 ● 二进制表示 package package01; import edu.princeton.cs.al ...

  4. 《算法》第五章部分程序 part 5

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,Knuth-Morris-Pratt 无回溯匹配,Boyer - Moore 无回溯匹配,Rabin - Karp 指纹匹配 ● Knuth-Morr ...

  5. 《算法》第五章部分程序 part 4

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,Trie 树类,Trie 集合,三值搜索树(Ternary Search Trie) ● Trie 树类 package package01; imp ...

  6. 《算法》第五章部分程序 part 2

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(计数 + 插排),(原地排序),(三路快排,与前面的三路归并排序相同) ● 计数 + 插排 package package01; ...

  7. 《算法》第五章部分程序 part 1

    ▶ 书中第五章部分程序,包括在加上自己补充的代码,字母表类,字符串低位优先排序(桶排) ● 字母表类 package package01; import edu.princeton.cs.algs4. ...

  8. Gradle 1.12用户指南翻译——第四十五章. 应用程序插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  9. 《算法》第一章部分程序 part 1

    ▶ 书中第一章部分程序,加上自己补充的代码,包括若干种二分搜索,寻找图上连通分量数的两种算法 ● 代码,二分搜索 package package01; import java.util.Arrays; ...

随机推荐

  1. MIME 设置

    1,打开iis7,选择你要设置网站,打开mime类型选项 2,找到.rar的mime类型,复制他的类型 3,复制后选项添加,在文件扩展名那一栏填入.*,然后在下面的mime类型复制你刚复制的appli ...

  2. Sql Server Report Service 的部署问题(Reporting Service 2014為什麼不需要IIS就可以運行)

    http://www.cnblogs.com/syfblog/p/4651621.html Sql Server Report Service 的部署问题 近期在研究SSRS部署问题,因为以前也用到过 ...

  3. @@identity与scope_identity()函数的区别

    @@IDENTITY 和SCOPE_IDENTITY 返回在当前会话中的任何表内所生成的最后一个标识值. SCOPE_IDENTITY 只返回插入到当前作用域中的值: @@IDENTITY 不受限于特 ...

  4. IO练习文件读取

    import java.io.*; public class CheckFile { private File f ; private BufferedReader bdr; private char ...

  5. JavaEE Web 开发 链接 mysql 出现 Class.not found的错误

    明明在项目的library里头导入过mysql-connector.jar的包,但是在加载driver的时候eclipse一直报这个错误.解决方案其实很简单,就是同样的jar包在tomcat的安装目录 ...

  6. 使用R语言-为矩阵(表格)的行列命名

    转自:http://www.dataguru.cn/article-2217-1.html R语言中经常进行矩阵(表格)数据的处理,在纷繁复杂的数据中,为其行列定义一个名字变得尤为重要.在处理巨量数据 ...

  7. java设计模式-State模式

    1.背景: MM的状态是非常不固定的,说不定刚才还非常高兴,没准一会就生气了.就跟六月的天似的,说变就变. 封装一下MM的状态:smile,cry,say:MM的状态决定了这些方法该怎么执行.   2 ...

  8. PAT 乙级 1041 考试座位号(15) C++版

    1041. 考试座位号(15) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 每个PAT考生在参加考试时都会被分 ...

  9. mariadb semi plugin遇到的坑

    安装完semi plugin运行一段时间后,重启mariadb, 突然发现canal无法解析数据了,一直在报错,然后登陆mariadb, show plugins竟然没有看到之前安装的semi plu ...

  10. HDOJ 2004 成绩转换

    #include<cstdio> #include<iostream> using namespace std; int main() { int score; while ( ...