后缀自动机的python实现
#!/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 deepcopy logger = 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 = trans class 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实现的更多相关文章
- 用c++后缀自动机实现最大公共字符串算法,并封装成Python库
后缀自动机的C++代码转自https://e-maxx.ru/algo/suffix_automata,其余封装为自写. 在C++文件同级目录建立setup.py文件,代码如下: # !/usr/bi ...
- BZOJ 后缀自动机四·重复旋律7
后缀自动机四·重复旋律7 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一段音乐旋律可以被表示为一段数构成的数列. 神奇的 ...
- 【Codeforces235C】Cyclical Quest 后缀自动机
C. Cyclical Quest time limit per test:3 seconds memory limit per test:512 megabytes input:standard i ...
- 【hihocoder#1413】Rikka with String 后缀自动机 + 差分
搞了一上午+接近一下午这个题,然后被屠了个稀烂,默默仰慕一晚上学会SAM的以及半天4道SAM的hxy大爷. 题目链接:http://hihocoder.com/problemset/problem/1 ...
- 【BZOJ-3998】弦论 后缀自动机
3998: [TJOI2015]弦论 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2018 Solved: 662[Submit][Status] ...
- HDU 4622 Reincarnation (查询一段字符串的不同子串个数,后缀自动机)
Reincarnation Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)To ...
- hihoCoder 后缀自动机三·重复旋律6
后缀自动机三·重复旋律6 时间限制:15000ms 单点时限:3000ms 内存限制:512MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数列. 现在小Hi ...
- hihoCoder #1445 : 后缀自动机二·重复旋律5
#1445 : 后缀自动机二·重复旋律5 时间限制:10000ms 单点时限:2000ms 内存限制:256MB 描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为一段数构成的数 ...
- 数据结构:后缀自动机 WJMZBMR讲稿的整理和注释
链接放在这里,有点难理解,至少我个人是的. 后缀自动机是一种有限状态自动机,其功能是识别字符串是否是母串的后缀.它能解决的问题当然不仅仅是判断是不是后缀这种事,跟字符串的连续子串有关的问题都可以往这个 ...
随机推荐
- Linux shell 提取文件名和目录名的一些方法(转)
很多时候在使用Linux的shell时,我们都需要对文件名或目录名进行处理,通常的操作是由路径中提取出文件名,从路径中提取出目录名,提取文件后缀名等等.例如,从路径/dir1/dir2/file.tx ...
- pt-query-digest 实践(转)
mysql slowlog 使用与介绍 slow_query_log =1-----是否打开 slow_query_log_file = /data/mysql_data/node-1/mysql-s ...
- mysql日常运维与参数调优
日常运维 DBA运维工作 日常 导数据,数据修改,表结构变更 加权限,问题处理 其它 数据库选型部署,设计,监控,备份,优化等 日常运维工作: 导数据及注意事项 数据修改及注意事项 表结构变更及注意事 ...
- java加载类的方法1.classloader 2.class.forName()
java加载类的方法1.classloader 2.class.forName() 加载一个类后,是在方法去创建这个类的元信息class对象,在方法区立刻创建.在方法区创建.
- Android Retrofit使用教程(三):Retrofit与RxJava初相逢
上一篇文章讲述了Retrofit的基本使用,包括GET,POST等请求.今天的文章中Retrofit要与RxJava配合使用. 了解RxJava RxJava有种种好处,我不在这里一一讲述.这里我只给 ...
- Git修改IP重新定位的方法
进入已clone项目的.git文件夹,打开config文件 打开config,如图显示,修改url中的IP为192.168.6.102,然后保存 在项目上右击选择属性(R),然后选择Git,即可看到当 ...
- 2016.7.12 eclipse和IDEA中mybatis generator插件的安装与使用
Eclipse中的安装 http://jingyan.baidu.com/article/9faa7231506ed8473c28cbee.html 1.下载插件 2.将插件generator的fea ...
- Android4.0(Phone)拨号启动过程分析(一)
因为工作的须要.须要改动原生的Phone程序,如今就好好看下来电与拨号是怎样处理的:无论是拨号还是来电,调用的都是Phone程序,因为非常多类都涉及到framework层,比較复杂:先从简单的拨号分析 ...
- 【Python】列表~深入篇
列表是一列按特定顺序排列的元素组成. 在Python中,用方括号[]来表示列表 下面是一个语言列表 Language = ['Chinese','English','Franch','Deutsch' ...
- HBase笔记
吴超 1.1 Hbase是Hadoop中的数据库,Hadoop还需要数据库吗?我们学的Hadoop是一个分布式的存储和计算的平台 为什么要在他上面建一个数据库呢,数据库是干什么的呢,数据库是一个管理系 ...