This commit is contained in:
Looly 2023-03-21 00:14:47 +08:00
parent 1247a59b19
commit 9d350acfb3
2 changed files with 166 additions and 165 deletions

View File

@ -3,11 +3,11 @@ package cn.hutool.core.lang.page;
/** /**
* 导航分页信息类<br> * 导航分页信息类<br>
* 根据提供的总页数每页记录数导航页码数等信息生成导航数组 * 根据提供的总页数每页记录数导航页码数等信息生成导航数组
* <pre> * <pre>{@code
* [1] 2 3 4 5 >> * [1] 2 3 4 5 >>
* << 1 [2] 3 4 5 >> * << 1 [2] 3 4 5 >>
* << 1 2 3 4 [5] * << 1 2 3 4 [5]
* </pre> * }</pre>
* *
* @author 莫取网名 * @author 莫取网名
*/ */

View File

@ -10,180 +10,181 @@ import java.util.*;
* @author renyp * @author renyp
*/ */
public class NFA { public class NFA {
/** /**
* AC树的根节点 * AC树的根节点
*/ */
private final Node root; private final Node root;
/** /**
* 标记是否需要构建AC自动机做树优化 * 标记是否需要构建AC自动机做树优化
*/ */
private volatile boolean needBuildAc; private volatile boolean needBuildAc;
/** /**
* 内置锁防止并发场景并行建AC树造成不可预知结果 * 内置锁防止并发场景并行建AC树造成不可预知结果
*/ */
private final Object buildAcLock; private final Object buildAcLock;
/** /**
* 内置锁防止并行插入新节点建立后被挂载到树上前 被篡改 * 内置锁防止并行插入新节点建立后被挂载到树上前 被篡改
*/ */
private final Object insertTreeLock; private final Object insertTreeLock;
/** /**
* 默认构造 * 默认构造
*/ */
public NFA() { public NFA() {
this.root = new Node(); this.root = new Node();
this.needBuildAc = true; this.needBuildAc = true;
this.buildAcLock = new Object(); this.buildAcLock = new Object();
this.insertTreeLock = new Object(); this.insertTreeLock = new Object();
} }
/** /**
* 构造函数 初始化词库 * 构造函数 初始化词库
* *
* @param words 添加的新词 * @param words 添加的新词
*/ */
public NFA(String... words) { public NFA(final String... words) {
this(); this();
this.insert(words); this.insert(words);
} }
/** /**
* 词库添加新词初始化查找树 * 词库添加新词初始化查找树
* *
* @param word 添加的新词 * @param word 添加的新词
*/ */
public void insert(String word) { public void insert(final String word) {
synchronized (insertTreeLock) { synchronized (insertTreeLock) {
needBuildAc = true; needBuildAc = true;
Node p = root; Node p = root;
for (char curr : word.toCharArray()) { for (final char curr : word.toCharArray()) {
int ind = curr; if (p.next.get((int) curr) == null) {
if (p.next.get(ind) == null) { p.next.put((int) curr, new Node());
p.next.put(ind, new Node()); }
} p = p.next.get((int) curr);
p = p.next.get(ind); }
} p.flag = true;
p.flag = true; p.str = word;
p.str = word; }
} }
}
/** /**
* 词库批量添加新词初始化查找树 * 词库批量添加新词初始化查找树
* *
* @param words 添加的新词 * @param words 添加的新词
*/ */
public void insert(String... words) { public void insert(final String... words) {
for (String word : words) { for (final String word : words) {
this.insert(word); this.insert(word);
} }
} }
/** /**
* 构建基于NFA模型的 AC自动机 * 构建基于NFA模型的 AC自动机
*/ */
private void buildAc() { private void buildAc() {
Queue<Node> queue = new LinkedList<>(); final Queue<Node> queue = new LinkedList<>();
Node p = root; final Node p = root;
for (Integer key : p.next.keySet()) { for (final Integer key : p.next.keySet()) {
p.next.get(key).fail = root; p.next.get(key).fail = root;
queue.offer(p.next.get(key)); queue.offer(p.next.get(key));
} }
while (!queue.isEmpty()) { while (!queue.isEmpty()) {
Node curr = queue.poll(); final Node curr = queue.poll();
for (Integer key : curr.next.keySet()) { for (final Integer key : curr.next.keySet()) {
Node fail = curr.fail; Node fail = curr.fail;
// 查找当前节点匹配失败他对应等效匹配的节点是哪个 // 查找当前节点匹配失败他对应等效匹配的节点是哪个
while (fail != null && fail.next.get(key) == null) { while (fail != null && fail.next.get(key) == null) {
fail = fail.fail; fail = fail.fail;
} }
// 代码到这有两种可能fail不为null说明找到了failfail为null没有找到那么就把fail指向root节点当到该节点匹配失败那么从root节点开始重新匹配 // 代码到这有两种可能fail不为null说明找到了failfail为null没有找到那么就把fail指向root节点当到该节点匹配失败那么从root节点开始重新匹配
if (fail != null) { if (fail != null) {
fail = fail.next.get(key); fail = fail.next.get(key);
} else { } else {
fail = root; fail = root;
} }
curr.next.get(key).fail = fail; curr.next.get(key).fail = fail;
queue.offer(curr.next.get(key)); queue.offer(curr.next.get(key));
} }
} }
needBuildAc = false; needBuildAc = false;
} }
/** /**
* @param text 查询的文本母串 * @param text 查询的文本母串
*/ * @return FoundWord列表查找到的所有关键词
public List<FoundWord> find(String text) { */
return this.find(text, true); public List<FoundWord> find(final String text) {
} return this.find(text, true);
}
/** /**
* @param text 查找的文本母串 * @param text 查找的文本母串
* @param isDensityMatch 是否密集匹配 * @param isDensityMatch 是否密集匹配
*/ * @return FoundWord列表查找到的所有关键词
public List<FoundWord> find(String text, boolean isDensityMatch) { */
// double check防止重复无用的 buildAC public List<FoundWord> find(final String text, final boolean isDensityMatch) {
if (needBuildAc) { // double check防止重复无用的 buildAC
synchronized (buildAcLock) { if (needBuildAc) {
if (needBuildAc) { synchronized (buildAcLock) {
this.buildAc(); if (needBuildAc) {
} this.buildAc();
} }
} }
List<FoundWord> ans = new ArrayList<>(); }
Node p = root, k = null; final List<FoundWord> ans = new ArrayList<>();
for (int i = 0, len = text.length(); i < len; i++) { Node p = root, k = null;
int ind = text.charAt(i); for (int i = 0, len = text.length(); i < len; i++) {
// 状态转移(沿着fail指针链接的链表此处区别于DFA模型) final int ind = text.charAt(i);
while (p != null && p.next.get(ind) == null) { // 状态转移(沿着fail指针链接的链表此处区别于DFA模型)
p = p.fail; while (p != null && p.next.get(ind) == null) {
} p = p.fail;
if (p == null) { }
p = root; if (p == null) {
} else { p = root;
p = p.next.get(ind); } else {
} p = p.next.get(ind);
// 提取结果(沿着fail指针链接的链表此处区别于DFA模型) }
k = p; // 提取结果(沿着fail指针链接的链表此处区别于DFA模型)
while (k != null) { k = p;
if (k.flag) { while (k != null) {
ans.add(new FoundWord(k.str, k.str, i - k.str.length() + 1, i)); if (k.flag) {
if (!isDensityMatch) { ans.add(new FoundWord(k.str, k.str, i - k.str.length() + 1, i));
p = root; if (!isDensityMatch) {
break; p = root;
} break;
} }
k = k.fail; }
} k = k.fail;
} }
return ans; }
} return ans;
}
private static class Node { private static class Node {
/** /**
* 当前节点是否是一个单词的结尾 * 当前节点是否是一个单词的结尾
*/ */
boolean flag; boolean flag;
/** /**
* 指向 当前节点匹配失败应该跳转的下个节点 * 指向 当前节点匹配失败应该跳转的下个节点
*/ */
Node fail; Node fail;
/** /**
* 以当前节点结尾的单词 * 以当前节点结尾的单词
*/ */
String str; String str;
/** /**
* 当前节点的子节点 * 当前节点的子节点
*/ */
Map<Integer, Node> next; Map<Integer, Node> next;
public Node() { public Node() {
this.flag = false; this.flag = false;
next = new HashMap<>(); next = new HashMap<>();
} }
} }
} }