后缀数组备忘

  SA写了就忘,SAM根本学不会,没救。

  SA模板:

int n,sa[N],sa2[N],rk[N<<1],tmp[N<<1],h[N],cnt[N],f[N][20],lg2[N];//sa[i] 在原串中排名第i的长度为k子串以sa[i]为开头(相同串在前面的排名靠前) rk[i] 在原串中以i为开头的长度为k子串排名第rk[i](相同串排名相同)//sa2[i] 辅助基数排序,在k+1~n+k中排名第i的长度为k子串以sa2[i]+k开头 tmp[i] 临时存储rk[i] cnt[i] 计数役 //f[i][j] st表,用以查询lcp lg2[i] 字面意思//注意rk和tmp开两倍
char s[N];
void make()
{
    int m=0;//本质不同长度为k的子串个数
    for (int i=1;i<=n;i++) cnt[rk[i]=s[i]]++,m=max(m,(int)s[i]);//名次数组中,初始排名即字符本身
    for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
    for (int i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;//后缀数组中,相同字符原串中在前面的排名靠前
    for (int k=1;k<=n;k<<=1)//对长度为2k的子串排序
    {
        int p=0;//计数役
        for (int i=n-k+1;i<=n;i++) sa2[++p]=i;//这些串为空串显然最靠前
        for (int i=1;i<=n;i++) if (sa[i]>k) sa2[++p]=sa[i]-k;//根据sa构造sa2,排名靠前的子串如果范围合法显然应该靠前
        memset(cnt,0,sizeof(cnt));
        for (int i=1;i<=n;i++) cnt[rk[i]]++;
        for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
        for (int i=n;i>=1;i--) sa[cnt[rk[sa2[i]]]--]=sa2[i];//基数排序,若前k位相同则后k位靠前的优先,因此以sa2倒序枚举
        memcpy(tmp,rk,sizeof(rk));
        p=rk[sa[1]]=1;
        for (int i=2;i<=n;i++)
        {
            if (tmp[sa[i]]!=tmp[sa[i-1]]||tmp[sa[i]+k]!=tmp[sa[i-1]+k]) p++;//判断与前一个是否本质不同,这里用到两倍数组
            rk[sa[i]]=p;
        }
        if (p==n) break;//全部本质不同说明排序完成
        m=p;
    }
    for (int i=1;i<=n;i++)
    {
        h[i]=max(h[i-1]-1,0);//重要性质,设h[i]为以i开头的后缀在名次数组中与上一后缀的lcp,则h[i]>=h[i-1]-1
        while (s[i+h[i]]==s[sa[rk[i-1]]+h[i]]) h[i]++;
    }
    for (int i=1;i<=n;i++) f[i][0]=h[sa[i]];
    lg2[1]=0;
    for (int i=2;i<=n;i++)
    {
        lg2[i]=lg2[i-1];
        if ((2<<lg2[i])<=i) lg2[i]++;
    }
    for (int j=1;j<20;j++)
        for (int i=1;i<=n;i++)
        f[i][j]=min(f[i][j-1],f[min(n,i+(1<<j-1))][j-1]);
}
int query(int x,int y)
{
    if (x>y) swap(x,y);
    x++;if (x>y) return N;
    return min(f[x][lg2[y-x+1]],f[y-(1<<lg2[y-x+1])+1][lg2[y-x+1]]);//lcp转化为区间rmq问题,排名第x的后缀和排名第y的后缀的lcp应为h[x+1]~h[y]的min
}

原文地址:https://www.cnblogs.com/Gloid/p/9845703.html

时间: 2024-11-10 13:42:13

后缀数组备忘的相关文章

Table view 备忘

Table view 备忘 本篇会以备忘为主,主要是一些基础的代理方法和数据源方法具体的优化好点子会后续跟上. Table view的数据源方法 必须实现的数据源方法 // 返回每一行的cell,可以做缓存处理,同样也可能会造成复用问题. func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // tableview 和 cell 都是在s

Objective-C教程备忘单

终极版本的Objective-C教程备忘单帮助你进行iOS开发. 想开始创建你的第一个iOS应用程序么?那么看一下这篇很棒的教程吧:Create your first iOS 7 Hello World Application 注:这篇文章我写了三天,可能在一些必要的地方使用了编辑和说明,所以如果有任何疑问和修改建议请在下方评论. 这不是一个初学者指南,也不是关于Objective-C的详细讨论,这是关于常见的和高水平的论题的快速索引. 如果这里有些问题没有涉及到,你也可以查阅以下文章: Obj

poj 3261 后缀数组 找重复出现k次的子串(子串可以重叠)

题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 但是大部分时间是在调试代码,因为模板的全局变量用混了,而自己又忘了,,,等西安邀请赛还有四省赛结束之后,该冷静反思下尝试拜托模板了 错误   :1.k用错,题目的k和模板的k用混; 2.还是二分的C()函数,这个其实跟前一篇<poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题>的C函数

MFC通过txt查找文件并进行复制-备忘

MFC基于对话框的Demo txt中每行一个23位的卡号. 文件夹中包含以卡号命名的图像文件.(fpt或者bmp文件) 要求遍历文件夹,找到txt中卡号所对应的图像文件,并复制出来. VC6.0写的. 太懒了,代码以前写过,直接复制就OK. **.cpp 下边的代码比较常用and改动比较大,备忘一下. void CRenameFileDlg::OnButton1() {//按钮1 UpdateData(); char buf[MAX_PATH] = {0}; if (select_any(buf

C++备忘

从<C++标准库>里面看到的一些技巧,以及自己遇到的一些技巧,备忘. 从流中读取数据存入容器 1 copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(vInput));//从cin中读取string 类型的数据存入 vInput 这个vector 更多迭代器:http://www.cnblogs.com/L-hq815/archive/2012/08/28/26607

poj 3261 后缀数组 找反复出现k次的子串(子串能够重叠)

题目:http://poj.org/problem?id=3261 仍然是后缀数组的典型应用----后缀数组+lcp+二分 做的蛮顺的,1A 可是大部分时间是在调试代码.由于模板的全局变量用混了,而自己又忘了.,,等西安邀请赛还有四省赛结束之后,该冷静反思下尝试拜托模板了 错误   :1.k用错,题目的k和模板的k用混; 2.还是二分的C()函数,这个事实上跟前一篇<poj 1226 hdu 1238 Substrings 求若干字符串正串及反串的最长公共子串 2002亚洲赛天津预选题>的C函

Android学习备忘笺01Activity

01.设置视图 在Android Studio新建的项目中,通过 setContentView(R.layout.activity_main);方法将res/layout/activity_main.xml 设置为MainActivity的视图,既可视化界面. 1 //IDE:Android Studio 2.3.1 2 3 @Override 4 protected void onCreate(Bundle savedInstanceState) { 5 super.onCreate(save

JqGrid相关操作备忘 方法列表

JqGrid相关操作备忘 方法列表 1.获得当前列表行数:$("#gridid").getGridParam("reccount"); 2.获取选中行数据(json):$("#gridid").jqGrid('getRowData', id); 3.刷新列表:$(refreshSelector).jqGrid('setGridParam', { url: ''), postData: ''}).trigger('reloadGrid'); 4.选

从源码包构建.deb文件的备忘

源码包是什么 我们知道ubuntu有别于gentoo之一的特点就是,gentoo是基于源码包安装的系统,而ubuntu是基于二进制的.我们执行一个apt-get install foo安装包命令时,apt从对应的apt source源地址下载一个二进制包-以.deb为后缀名的文件到/var/cache/apt/archives下,再用dpkg工具安装它们.这些.deb文件都是包的维护者在某台build machine上build之后放上去的,而与foo.deb对应的源码包,一般都是指三个文件的一