《算法》第五章部分程序 part 6
▶ 书中第五章部分程序,包括在加上自己补充的代码,非确定性有穷自动机(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的更多相关文章
- 《算法》第五章部分程序 part 3
▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(美国国旗排序) ● 美国国旗排序 package package01; import edu.princeton.cs.algs4 ...
- 《算法》第五章部分程序 part 8
▶ 书中第五章部分程序,包括在加上自己补充的代码,适用于基因序列的 2-Bit 压缩算法,行程长压缩算法,Huffman 压缩算法,LZW 压缩算法 ● 适用于基因序列的 2-Bit 压缩算法 pac ...
- 《算法》第五章部分程序 part 7
▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串的二进制表示.十六进制表示.图形表示 ● 二进制表示 package package01; import edu.princeton.cs.al ...
- 《算法》第五章部分程序 part 5
▶ 书中第五章部分程序,包括在加上自己补充的代码,Knuth-Morris-Pratt 无回溯匹配,Boyer - Moore 无回溯匹配,Rabin - Karp 指纹匹配 ● Knuth-Morr ...
- 《算法》第五章部分程序 part 4
▶ 书中第五章部分程序,包括在加上自己补充的代码,Trie 树类,Trie 集合,三值搜索树(Ternary Search Trie) ● Trie 树类 package package01; imp ...
- 《算法》第五章部分程序 part 2
▶ 书中第五章部分程序,包括在加上自己补充的代码,字符串高位优先排序(计数 + 插排),(原地排序),(三路快排,与前面的三路归并排序相同) ● 计数 + 插排 package package01; ...
- 《算法》第五章部分程序 part 1
▶ 书中第五章部分程序,包括在加上自己补充的代码,字母表类,字符串低位优先排序(桶排) ● 字母表类 package package01; import edu.princeton.cs.algs4. ...
- Gradle 1.12用户指南翻译——第四十五章. 应用程序插件
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- 《算法》第一章部分程序 part 1
▶ 书中第一章部分程序,加上自己补充的代码,包括若干种二分搜索,寻找图上连通分量数的两种算法 ● 代码,二分搜索 package package01; import java.util.Arrays; ...
随机推荐
- 源代码安装Apache、Mysql、PHP
源代码软件的优点: 获得最新版,能及时修复bug: 能自行修改和定制: 源代码打包形式: .tar.gz和.tar.bz2格式居多: 完整性校验: md5sum校验工具 ...
- Web API 令牌(秘钥是双方约定的,并不在网络连接上传输)
http://blog.csdn.net/qq289523052/article/details/47750021 秘钥是双方约定的,并不在网络连接上传输 Web API数据传输加密 2015-08- ...
- java.lang.OutOfMemoryError: Java heap space解决方法 (有问题咨询加微信)
支付宝扫码领取最高99元红包,到店支付15天,双十二瓜分15亿,打开支付宝首页搜“555176706”领红包,领到大红包的小伙伴赶紧使用哦! //首先检查程序有没有限入死循环 这个问题主要还是由这个问 ...
- mac打开文件提示文件已经坏了的修改
10.12下面,mac做了安全的限制,不能打开任意的文件,需要解除限制 sudo spctl --master-disable
- C#如何HttpWebRequest模拟登陆,获取服务端返回Cookie以便登录请求后使用
public static string GetCookie(string requestUrlString, Encoding encoding, ref CookieContainer cooki ...
- ribbon的注解使用报错--No instances available for [IP]
使用RestTemplate类调用其他系统的url的时候,加上ribbon的注解@LoadBalanced上这个注解之后访问,就报错了. 报错如下: 因为这里你不能直接访问地址,需要把地址改成你所调用 ...
- SVM的sklearn实现
转载:豆-Metcalf 1)SVM-LinearSVC.ipynb-线性分类SVM,iris数据集分类,正确率100% """ 功能:实现线性分类支持向量机 说明:可以 ...
- Hadoop错误集:Journal Storage Directory not formatted
类型一: 当你从异常信息中看到JournalNode not formatted,如果在异常中看到三个节点都提示需要格式化JournalNode. 如果你是新建集群,你可以重新格式化NameNode, ...
- uoj#213. 【UNR #1】争夺圣杯
http://uoj.ac/problem/209 单调栈求出每个位置x左边第一个大于它的位置L[x]和右第一个不小于它的位置R[x],于是矩形L[x]<=l<=x<=r<=R ...
- 如何做适合seo的404页面
我补充一点,404页面对于seo来说也是比较重要的,之所以不让跳转到首页就是楼上说的,容易被误判,所以,一般404页面的作用是引导客户点击进入首页. 实践证明,做了比较好的404页面对网站整体流量和排 ...