#1413 : Rikka with String 后缀自动机 + 二级差分
http://hihocoder.com/problemset/problem/1413?sid=1199641
这题断断续续做了2个多星期吧,一直不会
设总答案为sum,替换后新加的子串数量为x,失去的是y,那么每个位置的答案就是sum + x[i] - y[i]
首先可以知道如果把某个位置设置成'#',那么肯定有i * (len - i + 1)个新的不同的子串
比如是aa#cb,左边有i个选择,右边有len - i + 1个选择,根据组合数学就是i * (len - i + 1)个不同的子串
然后替换过后,就会有一些原本有的子串被删除了。
对于每一个状态,可以拓扑出它的mxpos和mipos也就是endpos的两个位置。
那么对于一个长度是len的子串,是否删除 了这个字符后 在整个字符串中不再出现,可以这样判断
如果mxpos - len + 1(也就是这个长度是len的子串的最大开始位置),如果这个位置还 < mipos那么如果删除了这个位置
肯定会丢失这个长度是len的字符串了。可以画个图吧看看
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;
const int maxn = 3e5 + , N = ;
struct SAM {
int mxCnt[maxn << ], son[maxn << ][N], fa[maxn << ], pos[maxn << ];
int flag[maxn << ][]; //是否前缀节点
int mi[maxn << ], mx[maxn << ];
int root, last, DFN, t;
int create() {
++t;
mxCnt[t] = pos[t] = fa[t] = NULL;
// mi[t] = inf, mx[t] = -inf;
for (int i = ; i < ; ++i) flag[t][i] = NULL;
for (int i = ; i < N; ++i) son[t][i] = NULL;
return t;
}
void init() {
++DFN;
t = , root = ;
last = create();
}
void addChar(int x, int _pos, int id) { // _pos表示在原串中的位置
int p = last;
int np = create();
last = np;
mxCnt[np] = mxCnt[p] + , pos[np] = _pos, flag[np][id] = DFN; //前缀节点
for (; p && son[p][x] == NULL; p = fa[p]) son[p][x] = np;
if (p == NULL) {
fa[np] = root;
return;
}
int q = son[p][x];
if (mxCnt[q] == mxCnt[p] + ) {
fa[np] = q;
return;
}
int nq = create(); //用来代替q的,默认不是前缀节点
flag[nq][id] = DFN - ; //默认不是前缀节点
pos[nq] = pos[q]; //pos要和q相同
for (int i = ; i < N; ++i) son[nq][i] = son[q][i];
fa[nq] = fa[q], mxCnt[nq] = mxCnt[p] + ;
fa[q] = nq, fa[np] = nq;
for (; p && son[p][x] == q; p = fa[p]) son[p][x] = nq;
}
int dp[maxn << ], in[maxn << ], que[maxn << ];
void topo() { //多次使用不用清空
for (int i = ; i <= t; ++i) {
in[fa[i]]++;
mi[i] = mx[i] = pos[i];
}
int head = , tail = ;
for (int i = ; i <= t; ++i) {
if (in[i] == ) que[tail++] = i;
}
while (head < tail) {
int cur = que[head++];
if (cur == root) break;
mx[fa[cur]] = max(mx[fa[cur]], mx[cur]);
in[fa[cur]]--;
if (in[fa[cur]] == ) que[tail++] = fa[cur];
}
}
} sam;
LL cnt[maxn], sub[maxn];
void add(int be, int en, LL val, LL d) {
cnt[be] += val;
cnt[en + ] -= d * (en - be) + val;
sub[be + ] += d;
sub[en + ] -= d;
}
void init(int en) {
for (int i = ; i <= en; ++i) {
sub[i] += sub[i - ];
cnt[i] += cnt[i - ] + sub[i];
}
} char str[maxn]; void work() {
int len;
cin >> len;
sam.init();
scanf("%s", str + );
LL ans = ;
for (int i = ; str[i]; ++i) {
sam.addChar(str[i] - 'a', i, );
}
sam.topo();
// int fuck = 5;
// printf("%d\n", sam.mx[fuck + 1]);
for (int i = ; i <= sam.t; ++i) {
ans += sam.mxCnt[i] - sam.mxCnt[sam.fa[i]];
if (sam.mx[i] - sam.mxCnt[i] + <= sam.mi[i]) {
int be = sam.mx[i] - sam.mxCnt[i] + ;
int en = min(sam.mx[i] - sam.mxCnt[sam.fa[i]], sam.mi[i]);
int len = en - be + ;
// printf("%d %d\n", be, en);
add(be, en, , );
be = en + ;
en = sam.mi[i]; //还有一段,要全部加上len个
// printf("%d %d\n\n", be, en); if (be <= en) add(be, en, len, ); }
}
init(len);
// printf("%d\n", cnt[4]);
for (int i = ; i <= len; ++i) {
printf("%lld ", ans + 1LL * (i) * (len - i + ) - cnt[i]);
} } int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
work();
return ;
}
#1413 : Rikka with String 后缀自动机 + 二级差分的更多相关文章
- 【hihocoder#1413】Rikka with String 后缀自动机 + 差分
搞了一上午+接近一下午这个题,然后被屠了个稀烂,默默仰慕一晚上学会SAM的以及半天4道SAM的hxy大爷. 题目链接:http://hihocoder.com/problemset/problem/1 ...
- HDU 6086 Rikka with String AC自动机 + DP
Rikka with String Problem Description As we know, Rikka is poor at math. Yuta is worrying about this ...
- 牛客多校第四场 I string 后缀自动机/回文自动机
这个回文自动机的板有问题,它虽然能过这道题,但是在计算size的时候会出锅! 题意: 求一个字符串中本质不同的连续子串有几个,但是某串和它反转后的字符串算一个. 题解: 要注意的是,一般字符串题中的“ ...
- hdu 6086 -- Rikka with String(AC自动机 + 状压DP)
题目链接 Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, s ...
- Codeforces 917F Substrings in a String - 后缀自动机 - 分块 - bitset - KMP
题目传送门 传送点I 传送点II 传送点III 题目大意 给定一个字母串,要求支持以下操作: 修改一个位置的字母 查询一段区间中,字符串$s$作为子串出现的次数 Solution 1 Bitset 每 ...
- 识别子串 (string)——后缀自动机+线段树
题目 [题目描述] 一般地,对于一个字符串 S,和 S 中第 $ i $ 个字符 x,定义子串 $ T=S(i.j) $ 为一个关于 x 的识别子申,当且仅当: 1.$ i \leq x \leq j ...
- cf1121F. Compress String(后缀自动机)
题意 题目链接 Sol 居然出个SAM板子也是没谁了233 #include<bits/stdc++.h> #define Pair pair<int, int> #defin ...
- Tjoi2019 甲苯先生和大中锋的字符串 后缀自动机_差分
tjoi胆子好大,直接出了两道送分题...... 都 9102 年了,还有省选出模板题QAQ...... Code: #include <bits/stdc++.h> #define se ...
- bzoj 5408: string 后缀自动机 + LCT
联赛前练练码力. code: #include <vector> #include <cstdio> #include <cstring> #include < ...
随机推荐
- WordCount 编码与测试
word count github 项目地址:https://github.com/liuqiang666/wordCount PSP表格 PSP2.1 PSP阶段 预估耗时(小时) 实际耗时( ...
- C++面试笔记--const、sizeof
首先来一个关于const的全面的解释,先看一波代码,之后再进行详细的分情况解释 ; const int *a=&b;//指向一个int常量的指针 int const *a=&b;//和 ...
- MySQL导出导入命令的用例
1.导出整个数据库 mysqldump -u 用户名 -p 数据库名 > 导出的文件名 mysqldump -u wcnc -p smgp_apps_wcnc > wcnc.sql 2.导 ...
- MediaRecorder录像那些事
最近在做一个项目需要运用到MediaRecorder的API,之前都没接触过这部分,开始着手弄的时候各种各样的问题,真是让人崩溃呀! 最后通过网上的资料和大神的指点,当然也有自己几天坚持不懈的努力,终 ...
- EchartJS平均线、最大值、最小值
1.先来看一个没有平均线.最大值.最小值的简单实例 option = { title: { text: '未来一周气温变化', subtext: '纯属虚构' }, tooltip: { trigge ...
- Delphi和C#数据类型对应表
Delphi DataType C# datatype ansistring string boolean bool byte byte char char comp double currency ...
- 使用metasploit进行栈溢出攻击-2
基本的栈溢出搞明白了,真实攻击中一个很重要的问题是shellcode生成. 利用Metasploit提供的工具,可以方便的生成shellcode,然后可以使用第一篇中的代码进行验证. 先说一下如何生成 ...
- Locust学习总结分享
简介: Locust是一个用于可扩展的,分布式的,性能测试的,开源的,用Python编写框架/工具,它非常容易使用,也非常好学.它的主要思想就是模拟一群用户将访问你的网站.每个用户的行为由你编写的py ...
- 关于Logger
Logger是我在各类编程语言中使用最多,同时也是改进最多的一个函数,今天在iOS下又折腾了一番,终于找到我想要的一个版本,这里做一个总结. python版 python对logger有专门的支持,只 ...
- iOS开发之蓝牙使用-建立连接的
1.大佬笔记 CSDN 2.代码 github