use crate::digraph::{directed_dfs::DirectedDFS, Digraph};
const DEBUG: bool = false;
pub struct NFA {
re: String,
g: Digraph,
m: usize,
}
impl NFA {
pub fn new(regexp: String) -> Self {
let mut ops = vec![];
let m = regexp.len();
let mut g = Digraph::new(m + 1);
let re = regexp.as_bytes();
for (i, c) in re.iter().enumerate() {
let mut lp = i;
match c {
b'(' | b'|' => {
ops.push(i);
}
b')' => {
let or = ops.pop().expect(")一定对应一个(,所以这里一定非none");
if re[or] == b'|' {
lp = ops.pop().expect("该位置一定是(");
assert_eq!(re[lp], b'(');
g.add_edge(lp, or + 1);
g.add_edge(or, i);
} else {
lp = or;
}
}
_ => {}
}
let after_p = i + 1;
if re.get(after_p) == Some(&b'*') {
g.add_edge(lp, after_p);
g.add_edge(after_p, lp);
}
match c {
b'(' | b'*' | b')' => {
g.add_edge(i, after_p);
}
_ => {}
}
}
if !ops.is_empty() {
panic!("ops:{:?}", ops)
}
if DEBUG {
print_nfs(re, &g);
}
NFA { re: regexp, g, m }
}
pub fn recognizes(&self, txt: &str) -> bool {
let mut pc = vec![];
let mut dfs: DirectedDFS = DirectedDFS::new(&self.g, 0);
for v in 0..self.g.vertex_count() {
if dfs.marked(v) {
pc.push(v);
}
}
for i in 0..txt.len() {
let mut match_info = vec![];
for &v in pc.iter() {
if let Some(&c) = self.re.as_bytes().get(v) {
if c == txt.as_bytes()[i] || c == b'.' {
match_info.push(v + 1);
}
}
}
pc = vec![];
dfs = DirectedDFS::new2(&self.g, match_info.iter());
for v in 0..self.g.vertex_count() {
if dfs.marked(v) {
pc.push(v);
}
}
if pc.is_empty() {
return false;
}
}
pc.iter().any(|&v| v == self.m)
}
}
pub fn print_nfs(re: &[u8], g: &Digraph) {
let mut labes = vec![];
for &c in re {
labes.push(char::from(c).to_string());
}
labes.push("end".to_string());
g.print_dot_with("re.dot", Some(labes));
}
#[cfg(test)]
mod test {
use crate::string::nfa::NFA;
#[test]
fn test() {
let nfa = NFA::new("(abc*d*|de.)".to_string());
assert!(nfa.recognizes("abccdd"));
assert!(nfa.recognizes("abcccc"));
assert!(!nfa.recognizes("abce"));
assert!(nfa.recognizes("abc"));
assert!(nfa.recognizes("abd"));
assert!(!nfa.recognizes("abf"));
assert!(!nfa.recognizes("de"));
assert!(nfa.recognizes("de."));
assert!(nfa.recognizes("def"));
assert!(!nfa.recognizes("deff"));
assert!(nfa.recognizes("abccdd"));
assert!(nfa.recognizes("abdd"));
}
}