MZL's Circle Zhou

题意:给定两个长度不超过a,b(1 <= |a|,|b| <= 90000),x为a的连续子串,b为y的连续子串(x和y均可以是空串);问x+y形成的不同串的个数?

误区:开始一门心思想着求出总的可形成的字符串个数,再减去a,b中相同的子串重叠的部分;想通过连续插入a+b得到的SAM并不能获得信息;因为x,y是任意的子串,连续插入导致一定是a的后缀和b的前缀

正解:直接在计算有不同子串时,就不去计算重复的 <=>对于一个可能出现x后缀和y前缀相同而重复计算的情况,就全部加入x而y变为去掉前缀的部分;

即找到x最后一个字符ch,使得在x后能加的字符串不以ch开头,这样就避免了重复的情况;(思想很好啊!!!)

对SAM的理解:

SAM将字符串插入之后,step[np] - step[pre[np]]表示 状态np比父节点pre[np]多出的以相同的点为后缀的不同子串的个数;

对SAM进行拓扑之后:

<1>如果按照字符串先遍历有效节点,初始化每个状态出现的次数为1,就可从孩子节点得到父节点的出现的次数;再使用step得到不同子串的数目即可求解一类与出现次数有关的问题;如 hdu 4641 K-string

<2>如果初始化root为1,递推出每个节点出现的次数 cnt[ g[p][v] ] += cnt[p]将得到的是从初始状态到当前状态的所有子串(当然是不同的)数;

本题:需要对a字符串构成的SAM的每一个状态(节点)找出g[u][j] == 0(即上面讲的x不可能有j这条状态转移边),这时如果y以j开头的不同子串的数量已知,那就可以直接往结果中贡献出 cnt[u] * C[j]的值(cnt[u]表示a字符串以x状态为后缀不同子串的个数,C[j]表示y中以j开头子串的数目)

cnt集合根据上面的性质2可以直接递推得到,那C集合呢?

其实就是在找到一个cnt[u]之后,如果u有一条j的转移边j,那么C[j]的值就增加了cnt[u]?

还有一个问题:SAM只能求得以某个点为后缀的,怎么得到从每个点开始的呢?

那就将b逆序插入。。(好思想啊)这样后缀就变成了后缀里面的前缀

对于b的各种情况已经计算完了,但是由于可以有空子串,a的不同子串还需加上;

坑:这道题数据很大,怀疑是% unsigned long long判的结果,使用long long  直接WA了

注:如果C集合写在SAM里面,在调用SA时运算的SA的C集合。。不是SB的

 #include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef unsigned long long ll; const int maxn = ;
const int SIGMA_SIZE = ; struct SAM{
int sz, last, pos[maxn<<];
int g[maxn<<][SIGMA_SIZE], pre[maxn<<], step[maxn<<];
ll cnt[maxn<<]; void init(){
sz = ;last = ;
newNode();
} void newNode(int s){
pre[++sz] = ;
step[sz] = s;
memset(g[sz],,sizeof(g[sz]));
} int idx(char ch){ return ch - 'a'; } void Insert(char ch);
void topoSort();
void preSolve(ll* C);
ll solve(ll* C); }SA, SB; void SAM::Insert(char ch){
newNode(step[last] + );
int v = idx(ch), np = sz, p = last;
while(p && !g[p][v]){
g[p][v] = np;
p = pre[p];
} if(p){
int q = g[p][v];
if(step[q] == step[p] + )
pre[np] = q;
else{
newNode(step[p] + );
int nq = sz;
for(int i = ;i < SIGMA_SIZE;i++)
g[nq][i] = g[q][i]; pre[nq] = pre[q];
pre[q] = pre[np] = nq; while(p && g[p][v] == q)
g[p][v] = nq, p = pre[p];
}
}
else pre[np] = ;
last = np;
} void SAM::topoSort(){
for(int i = ; i <= sz; i++) cnt[i] = ;
for(int i = ; i <= sz; i++) cnt[step[i]]++;
for(int i = ; i <= sz; i++) cnt[i] += cnt[i-];
for(int i = ; i <= sz; i++) pos[cnt[step[i]]--] = i;
} void SAM::preSolve(ll* C){
topoSort();
for(int i = ;i <= sz; i++) cnt[i] = ;
cnt[] = ; for(int i = ;i <= sz; i++){  //从root递推到每个节点出现的次数
int u = pos[i];
for(int j = ; j < SIGMA_SIZE; j++){
int v = g[u][j];
if(v == ) continue;
C[j] += cnt[u];
cnt[v] += cnt[u];
}
}
} ll SAM::solve(ll *C){
topoSort();
for(int i = ; i <= sz; i++) cnt[i] = ;
cnt[] = ; ll ret = ;
for(int i = ; i <= sz; i++){
int u = pos[i];
for(int j = ; j < SIGMA_SIZE; j++){
int v = g[u][j];
if(v == ) ret += cnt[u] * C[j];
else cnt[v] += cnt[u];
}
ret += cnt[u];    // x + ""
}
return ret;
} char str[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%s", str);
int n = strlen(str);
SA.init();
for(int i = ;i < n; i++)
SA.Insert(str[i]); scanf("%s", str);
n = strlen(str);
SB.init();
for(int i = n - ; i >= ; i--)    // **
SB.Insert(str[i]); ll C[SIGMA_SIZE];
memset(C,,sizeof(C));
SB.preSolve(C); printf("%llu\n",SA.solve(C));
}
}

hdu 5343 MZL's Circle Zhou SAM的更多相关文章

  1. HDU 5343 MZL's Circle Zhou

    MZL's Circle Zhou Time Limit: 1000ms Memory Limit: 131072KB This problem will be judged on HDU. Orig ...

  2. HDU 5343 MZL's Circle Zhou 后缀自动机+DP

    MZL's Circle Zhou Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Othe ...

  3. HDU5343 MZL's Circle Zhou(SAM+记忆化搜索)

    Problem Description MZL's Circle Zhou is good at solving some counting problems. One day, he comes u ...

  4. HDU5343:MZL's Circle Zhou(SAM,记忆化搜索DP)

    Description Input Output Sample Input Sample Output Solution 题意:给你两个串,分别从两个里面各选出一个子串拼到一起,问能构成多少个本质不同 ...

  5. [HDU5343]MZL's Circle Zhou

    题目大意: 给你两个字符串a和b,从中分别取出子串x和y,求不同的x+y的个数. 思路: 对于每一个字符串,构建SAM. 为了保证相同的x+y不会被重复统计,我们可以想办法只统计相同的x+y中x最长的 ...

  6. HDU 5351 MZL's Border (规律,大数)

    [HDU 5351 MZL's Border]题意 定义字符串$f_1=b,f_2=a,f_i=f_{i-1}f_{i-2}$. 对$f_n$的长度为$m$的前缀$s$, 求最大的$k$满足$s[1] ...

  7. Hdu 5352 MZL's City (多重匹配)

    题目链接: Hdu 5352 MZL's City 题目描述: 有n各节点,m个操作.刚开始的时候节点都是相互独立的,一共有三种操作: 1:把所有和x在一个连通块内的未重建过的点全部重建. 2:建立一 ...

  8. Hdu 5348 MZL's endless loop (dfs)

    题目链接: Hdu 5348 MZL's endless loop 题目描述: 给出一个无向图(有环,有重边),包含n个顶点,m条边,问能否给m条边指定方向,使每个顶点都满足abs(出度-入度)< ...

  9. hdu 5349 MZL's simple problem

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5349 MZL's simple problem Description A simple proble ...

随机推荐

  1. poj2388解题报告(排序)

    POJ 2388,题目链接http://poj.org/problem?id=2388 题意: 水题一道 给定n个数,输出中间值,可以用sort,干脆快捷. 代码: //396K 32MS #incl ...

  2. HTTP协议理解

    HTTP(Hyper Text Transfer Protocol)超文本传输协议,是一种请求响应式协议,类似两国会晤中需要遵守的规则.那么,其中有什么内容特点呢? HTTP特点: 1)支持客户端/服 ...

  3. [Visual Studio] 开启Visual Studio 2012通过右键菜单创建单元测试(Unit Test)

    Visual Studio 2012可以说是迄今为止微软VS开发工具中用户体验最好的产品,无论是速度还是体验以及功能,都非常出色,但是,使用了一段时间后发现有一个之前版本VS都有的功能却在Visual ...

  4. Mac上mariadb的启动与关闭

    1. 启动 launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist 2. 关闭 launchctl unload ~/Lib ...

  5. MAC下查看端口占用并杀死进程

    Eclipse在Run on Server时,Tomcat是开启的,但是报错,显示8080.8005和8009端口被占用 终端输入 查看所有开启的端口 sudo lsof -i -P | grep - ...

  6. H.264编码之IDCT变换原理

    上次讲到了DCT变换的推导过程,这次主要给大家讲下IDCT反变换的推导过程.建议大家先看上次讲的DCT变换公式推导过程.这样在看这篇文章时会容易很多!话不多说,让我们开始IDCT的旅程吧! IDCT反 ...

  7. 控制器view的延迟加载

  8. C# WPF 连接数据库Sqlhelper类

    从视频上学习到的WPF连接数据库的方法 需要配置一个 configuration 文件,包含 SQL instance 的相关信息 using System; using System.Collect ...

  9. JavaScript基础笔记一

    一.真假判断 真的:true.非零数字.非空字符串.非空对象 假的:false.数字零.空字符串.空对象.undefined 例: if(0){ alert(1) }else{ alert(2) } ...

  10. input两种默认显示文字方式

    First Note: placeholder属性 规定帮助用户填写输入字段的提示,值不会被提交, 且该提示会在输入字段为空时显示,并会在字段获得焦点时消失. 如 <input type=&qu ...