import java.util.List;
import java.util.Random;
publicclass RollingHash {
publicint m; // 法privateint r; // 基数public RollingHash(int m) {
this.m = m;
Random random = new Random();
this.r = random.nextInt(2, m);
}
publicint hash(List<Object> list) {
long l = r;
long h = 0;
for (Object e : list) {
h += xorshift(e.hashCode() + 1) * l;
h %= m;
l = l * r % m;
}
return (int) h;
}
publicint xorshift(int x) {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 15;
return Integer.remainderUnsigned(x, m);
}
}
S. Minato: "Zero-Suppressed BDDs and Their Applications", International Journal on Software Tools for Technology Transfer, Vol. 3, No. 2, pp. 156-170, Springer, May 2001.
各餅に対し「初めてサイズが2倍以上になる餅までの距離(存在しない場合は∞)」を並べた配列を二分探索や尺取法などで作成しておき、これを Range Max が で取れるデータ構造(Sparse Table 等)に載せておくことで、各クエリの決め打ち二分探索が で解けるようです*2。何故気が付かなかったのか…。
from collections import deque
sys.setrecursionlimit(200000) # enum() を使わないならコメントアウトしてOK。classAhoCorasick_Node:
def__init__(self, a=None) -> None:
self.a = a
self.son = {}
self.fail = self
self.output = None
self.nxt_output_node = self
self.dp = 0deffind_nxt(self):
if self.nxt_output_node is self:
return self
if self.nxt_output_node.output isNone:
self.nxt_output_node = self.nxt_output_node.find_nxt()
return self.nxt_output_node
classAhoCorasick:
def__init__(self, words) -> None:
self.words = words
self.head = AhoCorasick_Node()
self._build_Trie()
self._set_fail()
def_build_Trie(self):
for w_idx, word inenumerate(self.words):
node = self.head
for a in word:
if a notin node.son:
node.son[a] = AhoCorasick_Node(a)
node = node.son[a]
node.output = w_idx
def_set_fail(self):
dq = deque([self.head])
while dq:
now = dq.pop()
for a, nxt in now.son.items():
node = now.fail
while a notin node.son and node.fail isnot node:
node = node.fail
if a in node.son and node.son[a] isnot nxt:
nxt.fail = node.son[a]
else:
nxt.fail = self.head
nxt.nxt_output_node = nxt.fail
dq.appendleft(nxt)
defenum(self, text):
node = self.head
ret = [[] for _ inrange(len(self.words))]
for i, a inenumerate(text):
while a notin node.son and node.fail isnot node:
node = node.fail
if a in node.son:
node = node.son[a]
if node.output isnotNone:
w_idx = node.output
ret[w_idx].append(i+1-len(self.words[w_idx]))
output_node = node
while output_node.find_nxt() isnot self.head:
output_node = output_node.nxt_output_node
w_idx = output_node.output
ret[w_idx].append(i+1-len(self.words[w_idx]))
return ret
defcnt(self, text):
tps = []
dq = deque([self.head])
while dq:
now = dq.pop()
now.dp = 0
tps.append(now)
for nxt in now.son.values():
dq.appendleft(nxt)
node = self.head
ret = [0]*len(self.words)
for a in text:
while a notin node.son and node.fail isnot node:
node = node.fail
if a in node.son:
node = node.son[a]
node.dp += 1for node inreversed(tps[1:]):
if node.output isnotNone:
w_idx = node.output
ret[w_idx] += node.dp
node.fail.dp += node.dp
return ret