▶ 书中第五章部分程序,包括在加上自己补充的代码,非确定性有穷自动机(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. C++Builder 网站。记住学习

    http://www.ccrun.com/ C++Builder

  2. solr4.9+tomcat7 multiCore

    ES不支持groupby,于是想看看solr怎么实现的. 搭建环境: 1)下载tomcat7.solr4.9,解压: 2)配置tomcat7的端口和URIEncoding(utf-8): 3)拷贝so ...

  3. bzoj4980: 第一题

    Description 神犇xzyo听说sl很弱,于是出了一题来虐一虐sl.一个长度为2n(可能有前缀0)的非负整数x是good的,当且仅当 存在两个长度为n(可能有前缀0)的非负整数a.b满足a+b ...

  4. Hadoop是怎么分块Block的?

    不多说,直接上干货! hadoop的分块有两部分. 第一部分就是数据的划分(即把File划分成Block),这个是物理上真真实实的进行了划分,数据文件上传到HDFS里的时候,需要划分成一块一块,每块的 ...

  5. java的应用包的方法,及调用类里面函数的原理

    selenium官网下载的selenium包 包导入eclipse 见:https://www.cnblogs.com/kaibindirver/p/10674604.html 代码

  6. levenshtein函数

    Levenshtein算法已在部分DBMS中实现. (例如:PostgreSQL:http://www.postgresql.org/docs/9.1/Static/fuzzystrmedi.html ...

  7. mina2中的session

    简介 session类图 Mina每建立一个连接同时会创建一个session对象,用于保存这次读写需要用到的所有信息.从抽象类AbstractIoSession中可以看出session具有如下功能: ...

  8. [转]VB 读写ini 配置文件

    转自 百度知道 C# 读写 ini配置文件 点此链接 'API 声明Public Declare Function GetPrivateProfileString Lib "kernel32 ...

  9. 个人知识管理PKM:收集、消化、应用、创新

                                                          个人知识管理PKM:收集.消化.应用.创新 准备工作1.制作知识分类体系(在线博客分类.本地 ...

  10. [UE4]裁剪 Clipping

    Clipping裁剪,是每个UI都有的属性.一般是在容器UI上设置,对容器内的UI进行裁剪. 一.Clip to Bounds:裁剪到边界 二.Clip To Bounds - Without Int ...