首页 技术 正文
技术 2022年11月7日
0 收藏 483 点赞 1,074 浏览 3303 个字
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2019-02-25 14:32:50
# @Author : Sheldon (thisisscret@qq.com)
# @Blog : 谢耳朵的派森笔记
# @Link : https://www.cnblogs.com/shld/
# @Version : 0.0.1
from collections import OrderedDict
import logging
from copy import deepcopylogger = logging.getLogger("sam")
logger.propagate = False
logger.handlers.clear()
logger.setLevel(level=logging.INFO)
# handler = logging.FileHandler("log.txt")
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)class State:
def __init__(self, length=None, link=None, trans=None):
self.length = length
self.link = link
self.trans = transclass Sam:
def __init__(self, s=""):
self.last = 0
self.sz = 1
self.st = OrderedDict({0: State(0, -1, OrderedDict())})
self.samstr = ""
self(s) @staticmethod
def _dict2str(dct):
return ' '.join(f'{k}->{w}' for k, w in dct.items()) def _extend(self, c):
cur = self.sz self.st[cur] = State(None, None, OrderedDict())
self.st[cur].length = self.st[self.last].length + 1
logger.debug(f"状态{cur}添加到后缀自动机")
logger.debug(f"上一个节点状态是{self.last}")
logger.debug(f"状态{cur}的长度是{self.st[self.last].length + 1}") # 如果后缀自动机最近转移里面没有当前字符,则添加该字符,并将状态指向当前状态
# 继续沿着后缀连接走,进行上述操作直到到达第一个状态或者转移中有此字符
p = self.last while p > -1 and self.st[p].trans.get(c) is None:
logger.debug(
f"状态{p}的转移:{self._dict2str(self.st[p].trans)}{('','不')[self.st[p].trans.get(c) is None]}包含字符{c}") self.st[p].trans[c] = cur
logger.debug(f"把{c}添加进状态{p}的转移")
logger.debug(f"开始查找状态{p}的后缀链接...")
p = self.st[p].link
logger.debug(f"后缀链接为状态{p}") # 如果后缀链接走到底了,没有相同的,则后缀链接指向0状态,即空字符串
if p == -1:
self.st[cur].link = 0
logger.debug(f"状态{cur}沿后缀链接走到了初始状态,将其的后缀链接指向状态0") # 如果找到上一状态的转移里有c字符,找到转移c的另一状态
else:
q = self.st[p].trans[c]
logger.debug(f"在状态{p}的转移中找到了字符{c},{c}指向状态{q}")
# 如果q状态与p状态相连,则当前状态的后缀链接指向q状态
if self.st[p].length + 1 == self.st[q].length:
self.st[cur].link = q
logger.debug(f"状态{p}的长度比状态{q}少1,把当前状态{cur}的后缀链接指向状态{q}")
# 如果不相连则开一个新状态,长度为p状态的下一个状态,后缀链接与转移指向q
# 搜索状态p,若c转移为q,则指向新状态,并搜索后缀链接的状态重复指向新状态
# 直到状态转移不为q,跳出
else:
self.sz += 1
logger.debug(f"状态{p}的长度与状态{q}的长度不连续,新建状态{self.sz}")
self.st[self.sz] = State(self.st[p].length + 1, self.st[q].link, deepcopy(self.st[q].trans))
logger.debug(f"新状态{self.sz}的长度为状态{p}的长度加1")
while p > -1 and self.st[p].trans.get(c) == q:
logger.debug(f"状态{p}转移中{c}的转移是状态{q},把指向{q}的转移改为指向新状态{self.sz}")
self.st[p].trans[c] = self.sz
logger.debug(f"查找状态{p}的后缀链接...")
p = self.st[p].link
logger.debug(f"后缀链接为状态{p}")
# 把当前状态与q的后缀链接指向新状态
self.st[q].link = self.st[cur].link = self.sz
logger.debug(f"把状态{q}与当前状态{cur}的后缀链接指向新状态{self.sz}")
logger.debug(f"当前状态{cur}的长度为:{self.st[cur].length}")
logger.debug(f"当前状态{cur}的后缀链接为:{self.st[cur].link}")
logger.debug(f"当前状态{cur}的转移为:{self._dict2str(self.st[cur].trans)}")
logger.debug(f"当前状态{cur}建立完成")
logger.debug("\n")
# 状态索引占位
self.sz += 1
self.last = cur
self.samstr += c def __add__(self, addition):
new_sam = deepcopy(self)
if isinstance(addition, str):
for c in addition:
new_sam._extend(c)
elif isinstance(addition, Sam):
for c in addition.samstr:
new_sam._extend(c)
return new_sam def __call__(self, s):
for c in s:
self._extend(c) def __str__(self):
pts = ""
for k, w in self.st.items():
pts += f"状态{k}: 长度:{w.length} 后缀链接-->{w.link} 转移:{self._dict2str(w.trans)}\n"
return pts def __getitem__(self, key):
return self.st[key]if __name__ == "__main__": s1 = "aabbabd"
s2 = "rtrbWHWUKkkh"
sam1 = Sam(s1)
sam2 = Sam(s2) print(sam1) #加的时候不会改变原后缀自动机
sam3 = sam1+sam2
sam4 = sam1+s2 #调用则在原自动机上更新
sam1(s2)
  • 调试的时候日志级别设为DEBUG就行了
  • 有空的时候更新相应字符串处理的算法
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,078
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,553
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,402
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,177
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,814
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,898