#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测试小程序的实现
一.GitHub地址: https://github.com/245553473/WordCount 二.PSP表格: PSP PSP阶段 预估耗时(分钟) 实际耗时(分钟) Planning 计划 ...
- java的import关键字的使用
在java中如何使用Java包中自带的类呢? 方法一: 在使用时可以用Java.(包名).(方法名).(包中的类名): 例如:Java.util.Arrays.toString(某个要排序数组); 具 ...
- Netty学习大纲
1.BIO.NIO和AIO2.Netty 的各大组件3.Netty的线程模型4.TCP 粘包/拆包的原因及解决方法5.了解哪几种序列化协议?包括使用场景和如何去选择6.Netty的零拷贝实现7.Net ...
- Windows系统编程之进程同步试验
试验过程中调用了不少系统函数,并且涉及到一些系统级的概念,在此记录下来做为解决问题的一种方式.也许在以后的编程的过程中是否可以通过调用系统平台上的东西来完成一些任务,这仍不失为一种好的思维方式. 多线 ...
- MacBook Pro (13 英寸, 2012 年中)安装win7系统
准备: windows7 ISO镜像 16G或更大U盘(提前备份,需要格式化) Apple 官方提供的 windows7驱动程序 详细步骤: 1.打开Bootcamp,选择前两个选择点击继续,选择下载 ...
- unity编辑器拓展
[ExecutelnEditMode] 在EditMode下也可以执行脚本,Unity默认情况下,脚本只有运行时被执行,加上此属性后,不运行程序也能执行.与PlayMode不同的是函数不会不停 ...
- 动态横向(水平)合并GridView数据行DataRow的列
前一段时间,Insus.NET有写过<动态合并GridView数据行DataRow的列>http://www.cnblogs.com/insus/p/3238348.html, 那是纵向( ...
- 解决VMware Workstation 不可恢复错误: (vcpu-0)
转载:http://tieba.baidu.com/p/3487673152 如图的错误 如果你按照破解了mac支持的VMware Workstation 11的新建虚拟机向导一步一步创建了一个mac ...
- Miller-Rabin素数测试
Miller-Rabin素数测试 给出一个小于1e18的数,问它是否为质数?不超过50组询问.hihocoder 我是真的菜,为了不误导他人,本篇仅供个人使用. 首先,一个1e18的数,朴素\(O(\ ...
- 剑指offer —— 二维数组的查找
1.问题:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 2.思路:只看题目本身 ...