【51nod1600】Simple KMP(SAM)(LCT)(差分)

2024-01-30 01:18
文章标签 差分 kmp simple sam lct 51nod1600

本文主要是介绍【51nod1600】Simple KMP(SAM)(LCT)(差分),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题意:

对于一个字符串 ∣ S ∣ |S| S,我们定义 f a i l [ i ] fail[i] fail[i],表示最大的 x 使得 S [ 1.. x ] = S [ i − x + 1.. i ] S[1..x]=S[i-x+1..i] S[1..x]=S[ix+1..i],满足 ( x < i ) (x<i) (x<i)
显然对于一个字符串,如果我们将每个 0 < = i < = ∣ S ∣ 0<=i<=|S| 0<=i<=S 看成一个结点,除了 i = 0 i=0 i=0 以外 i i i f a i l [ i ] fail[i] fail[i] 连边,这是一颗树的形状,根是 0
我们定义这棵树是 G ( S ) G(S) G(S),设 f ( S ) f(S) f(S) G ( S ) G(S) G(S) 中除了 0 号点以外所有点的深度之和
其中 0 号点的深度为 -1
定义 k e y ( S ) key(S) key(S) 等于 S S S 的所有非空子串 S ′ S' S f ( S ′ ) f(S') f(S) 之和
给定一个字符串S,现在你要实现以下几种操作:
1.在 S S S最后面加一个字符
2.询问 k e y ( S ) key(S) key(S)


好题!
考虑新加一个字符的贡献,就是考虑所有的后缀的新增贡献

注意到,某一个后缀的新增贡献,就是它往上跳一个 f a i l fail fail,然后剩下的就全部是跳上去过后的那个点的贡献,而这个点在之前作为后缀出现过,所以我们只需要算出一个增量的差分数组

每一个后缀的新增贡献是它在 G ( S ) G(S) G(S) 中的深度
注意到统计一棵树的深度和可以等价于统计每个点的 s i z e size size
也就是这个套路
∑ i i ∗ ∑ d e p = i 1 = ∑ i ∑ d e p ≥ i 1 \sum_ii*\sum_{dep=i}1=\sum_i\sum_{dep\ge i}1 iidep=i1=idepi1
又注意到这个就等价于对每一个后缀统计它的贡献,需要算它出现了多少次
就是把 f a i l fail fail 链提出来,对每个点的贡献就是 ( l e n [ x ] − l e n [ f a [ x ] ] ) ∗ s i z [ x ] (len[x]-len[fa[x]])*siz[x] (len[x]len[fa[x]])siz[x]
需要支持换父亲,链加, L C T LCT LCT 即可
感觉差分,从上一步走来,就像是站在巨人的肩膀上


#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 2e5 + 5;
cs int Mod = 1e9+7;
typedef long long ll;
int add(int a, int b){ return a+b>=Mod?a+b-Mod:a+b; }
int mul(int a, int b){ return (ll)a*b%Mod; }
void Add(int &a, int b){ a = add(a, b); }
int n; char s[N];
namespace LCT{int ch[N][2], fa[N], r[N], tg[N], len[N], sm[N], vl[N];bool isr(int x){ return ch[fa[x]][0] != x && ch[fa[x]][1] != x; }int get(int x){ return ch[fa[x]][1] == x; }void pushup(int x){sm[x]=sm[ch[x][0]]+len[x]+sm[ch[x][1]];vl[x]=add(add(vl[ch[x][0]],vl[ch[x][1]]),mul(r[x],len[x]));}void tag(int x, int v){ if(!x) return; Add(r[x],v); Add(tg[x],v); Add(vl[x],mul(sm[x],v)); }void down(int x){ if(tg[x]) tag(ch[x][0],tg[x]), tag(ch[x][1],tg[x]),tg[x]=0; }void path(int x){ if(!isr(x)) path(fa[x]); down(x); }void rot(int x){int y=fa[x],z=fa[y],k=get(x); if(!isr(y)) ch[z][get(y)]=x; fa[x]=z;ch[y][k]=ch[x][k^1]; fa[ch[x][k^1]]=y; ch[x][k^1]=y; fa[y]=x; pushup(y); pushup(x);}void spl(int x){ path(x); while(!isr(x)){ int y=fa[x]; if(!isr(y)) rot(get(x)^get(y)?x:y); rot(x); } }void acs(int x){ for(int y=0;x;y=x,x=fa[x]) spl(x),ch[x][1]=y,pushup(x); }void link(int x, int y){ acs(x); spl(x); fa[x] = y; }void cut(int x, int y){ acs(x); spl(x); spl(y); ch[y][1]=fa[x]=0; pushup(y); }
}
namespace SAM{int ch[N][26], lk[N], r[N], len[N], las=1, node=1;void init(int x){LCT::len[x]=len[x]-len[lk[x]];LCT::r[x]=r[x]; LCT::pushup(x);}int extend(int c){int now = ++node, p = las; len[now]=len[p]+1; r[now]=1;for(;p&&!ch[p][c];p=lk[p]) ch[p][c]=now;if(!p) lk[now]=1, init(now), LCT::link(now,1);else{int q = ch[p][c]; if(len[q]==len[p]+1) lk[now]=q, init(now), LCT::link(now,q);else{int cl = ++node; len[cl]=len[p]+1; lk[cl]=lk[q]; memcpy(ch[cl],ch[q],sizeof(ch[q]));LCT::acs(q); LCT::spl(q);r[cl]=r[q]=LCT::r[q];init(cl);LCT::link(cl,lk[cl]);LCT::cut(q,lk[q]);lk[now]=lk[q]=cl;init(q); init(now);LCT::link(now,lk[now]);LCT::link(q,lk[q]);for(;p&&ch[p][c]==q;p=lk[p]) ch[p][c]=cl;}} las = now;LCT::acs(now); LCT::spl(now); int ps=LCT::ch[now][0], delta=LCT::vl[ps]; LCT::tag(ps,1); return delta;}
}
int main(){scanf("%s",s+1); n=strlen(s+1);int res = 0, ans = 0;for(int i = 1; i <= n; i++){Add(res, SAM::extend(s[i]-'a'));Add(ans, res); cout << ans << '\n';} return 0;
}

这篇关于【51nod1600】Simple KMP(SAM)(LCT)(差分)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/658690

相关文章

uva 10014 Simple calculations(数学推导)

直接按照题意来推导最后的结果就行了。 开始的时候只做到了第一个推导,第二次没有继续下去。 代码: #include<stdio.h>int main(){int T, n, i;double a, aa, sum, temp, ans;scanf("%d", &T);while(T--){scanf("%d", &n);scanf("%lf", &first);scanf

poj 3159 (spfa差分约束最短路) poj 1201

poj 3159: 题意: 每次给出b比a多不多于c个糖果,求n最多比1多多少个糖果。 解析: 差分约束。 这个博客讲差分约束讲的比较好: http://www.cnblogs.com/void/archive/2011/08/26/2153928.html 套个spfa。 代码: #include <iostream>#include <cstdio>#i

poj 3169 spfa 差分约束

题意: 给n只牛,这些牛有些关系。 ml个关系:fr 与 to 牛间的距离要小于等于 cost。 md个关系:fr 与 to 牛间的距离要大于等于 cost。 隐含关系: d[ i ] <= d[ i + 1 ] 解析: 用以上关系建图,求1-n间最短路即可。 新学了一种建图的方法。。。。。。 代码: #include <iostream>#include

codeforces535D:Tavas and Malekas(KMP)

(i-1 , i)有重合的时候 ,从第i位开始的子串必须是模式串的前缀。 而同时,从第i位开始的子串本来就已经是模式串的后缀了。 typedef long long LL ;const int maxn = 1000008 ;int next[maxn] ;void getnext(char s[]){int len = strlen(s) ;next[0] = -1 ;i

POJ 1364差分约束

给出n个变量,m个约束公式 Sa + Sa+1 + .... + Sa+b < ki or > ki ,叫你判断是否存在着解满足这m组约束公式。 Sa + Sa+1   +   .+ Sa+b =  Sum[a+b] - Sum[a-1]  . 注意加入源点n+1 。 public class Main {public static void main(Strin

fzu 2275 Game KMP

Problem 2275 Game Time Limit: 1000 mSec    Memory Limit : 262144 KB  Problem Description Alice and Bob is playing a game. Each of them has a number. Alice’s number is A, and Bob’s number i

【LVI-SAM】激光雷达点云处理特征提取LIO-SAM 之FeatureExtraction实现细节

激光雷达点云处理特征提取LIO-SAM 之FeatureExtraction实现细节 1. 特征提取实现过程总结1.0 特征提取过程小结1.1 类 `FeatureExtraction` 的整体结构与作用1.2 详细特征提取的过程1. 平滑度计算(`calculateSmoothness()`)2. 标记遮挡点(`markOccludedPoints()`)3. 特征提取(`extractF

Python中差分进化differential_evolution的调用及参数说明

在场景应用中,要求我们的函数计算结果尽可能的逼近实际测量结果,可转化计算结果与测量结果的残差,通过最小化残差,便可求出最优的结果。但使用最小二乘等方法来计算时,常常会使迭代的结果显然局部最优点而导致结算错误。 差分进化原理 差分进化(Differential Evolution,DE)是一种基于群体差异的进化算法,其计算思想主要包括以下几个方面: 一、初始化种群 首先,随机生成一个初始种群

使用django-simple-captcha遇到的坑

使用django-simple-captcha遇到的坑 一站点gongshare.com在做注册功能时验证码采用的django-simple-captcha,因为笔者开发环境采用的Windows 64bit系统,结果安装使用的时候接二连三遇到好几个坑。 django-simple-captcha需要依赖django1.3+、PIL1.1.7+或者Pillow2.0+,根据文档安装后开始使用时,

KMP 详解

KMP数组存的是什么 对于一个字符串 b,下标从1开始。 则kmp[i]表示 以i结尾的连续子串 = s的前缀的最大值(等价于前缀最大结尾处) 如何求KMP 假设 i 以前的KMP都被求出来了。 j 表示上一个字符可以成功匹配的长度(等价于下标) 如果b[j+1] != b[i]下一个位置匹配不上(即不能成为前缀) 则,让j = kmp[j] 即成为以j结尾的 连续子串 的 最长前缀