题面:洛谷

题解:

  因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献。

  所以子串这个限制相当于是没有的。

  所以我们只需要对于每个位置i求出f[i]表示以i为开头的形如BB这样的串的个数,g[i]表示以i为结尾的形如AA这样的串的个数即可。

  考虑分别处理这2个数组。

  我们可以枚举AA(BB)这样的串中A(B)的长度l,然后把原串每l个字符放在一个块中,在考虑统计答案。

  先考虑这样一个问题:

    假如固定一个串的结尾,再枚举这个串A的长度,怎样可以判断是否合法?

    实际上我们只需要判断我们假定的这个AA串的开头和中间位置(结尾向前走A的长度)的LCP是否可以覆盖开头到中间即可。

  然后如果我们已经把原串对于当前枚举的长度l分成了很多块,其实我们就已经可以对与每个块的开头结尾所代表的点对(i, j)判断是否可以产生贡献了。

  但是怎么统计 其他没有刚好对应在某个块的开头结尾的点对 的贡献呢?

  表示并没有想出来,,,但是感觉有个blog写的很好,,,

  推荐一下:[BZOJ]4650 优秀的拆分(Noi2016)

  以后彻底搞懂了再来填坑吧。

 #include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 301000
#define ac 602000
#define LL long long
#define rev reverse
#define mem(x) memset(x, 0, sizeof(x)) int T, n, m;
int h[ac], sa[ac], p1[ac], p2[ac], b[ac], d[ac];
int rk[ac], p[AC], t[AC], rk1[ac];
int st1[AC][], st2[AC][];
int f[AC], g[AC];
LL ans;
char s[AC]; void init()
{
for(R i = ; i <= n; i ++) f[i] = g[i] = rk[i] = ;//因为有多组数据,所以要全部清空
}//mem这么多次还不如for inline void upmax(int &a, int b){
if(b > a) a = b;
} inline int Min(int a, int b){
return (a < b) ? a : b;
} void pre()
{
scanf("%s", s + ), n = strlen(s + ), m = ;
} void ssort()
{
for(R i = ; i <= n; i ++) ++ d[p2[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = ; i <= n; i ++) b[d[p2[i]] --] = i;//给i分配d[p2[i]]的排名
for(R i = ; i <= m; i ++) d[i] = ; for(R i = ; i <= n; i ++) ++ d[p1[i]];
for(R i = ; i <= m; i ++) d[i] += d[i - ];
for(R i = n; i; -- i) sa[d[p1[b[i]]] --] = b[i];//给b[i]分配d[p1[b[i]]]的排名
for(R i = ; i <= m; i ++) d[i] = ;
} void get_sa()
{
for(R i = ; i <= n; i ++) sa[i] = i, rk[i] = s[i];//初始化
m = ;//这个也要重置
for(R k = ; k <= n; k <<= )
{
for(R i = ; i <= n; i ++) p1[i] = rk[i], p2[i] = rk[i + k];
ssort();
int tmp = ;
rk[sa[]] = ;
for(R i = ; i <= n; i ++)
rk[sa[i]] = (p1[sa[i]] == p1[sa[i - ]] && p2[sa[i]] == p2[sa[i - ]]) ? tmp : ++ tmp;
if(tmp >= n) break;
m = tmp;//忘了,,,
}
} void build()//获取h数组
{
//memset(h, 0, sizeof(h));
for(R i = , k = ; i <= n; i ++)
{
if(k) -- k;
int j = sa[rk[i] - ];
while(s[i + k] == s[j + k]) ++ k;
h[rk[i]] = k;
}
} #define st st1
void build1()//建st1(维护LCP)
{
int tmp = , cnt = ;
memcpy(rk1, rk, sizeof(rk));
for(R i = ; i <= n; i ++)
{
st[i][] = h[i];
if(i == tmp << ) tmp <<= , ++ cnt;
p[i] = tmp, t[i] = cnt;
}
}
#undef st void build2()//建st2(维护LCS)改成st1, st2一起建了。。。。
{
for(R i = ; i <= n; i ++) st2[i][] = h[i];
int tmp = ;
for(R i = ; i <= ; i ++)
{
for(R j = ; j <= n; j ++)
{
st1[j][i] = Min(st1[j][i - ], st1[j + tmp][i - ]);
st2[j][i] = Min(st2[j][i - ], st2[j + tmp][i - ]);
}
tmp <<= ;
}
} inline void swap(int &l, int &r)
{
int x = l;
l = r, r = x;
} int get1(int l, int r)//查询串l和串r的LCP
{
l = rk1[l], r = rk1[r];
if(l > r) swap(l, r);
++ l;
int len = r - l + ;
return Min(st1[l][t[len]], st1[r - p[len] + ][t[len]]);
} int get2(int l, int r)//查询串l和串r的LCS
{//因为是翻转过来求的,所以查询要翻转一下
l = n - l + , r = n - r + ;
l = rk[l], r = rk[r];
if(l > r) swap(l, r);
++ l;
int len = r - l + ;
return Min(st2[l][t[len]], st2[r - p[len] + ][t[len]]);
} void get()
{
int lim = n << ;
for(R k = ; k < lim; k ++)//枚举A的长度
{
int maxn = , maxn2 = ;
for(R i = ; i <= n; i += k)
{
int j = i + k;//j为下一段开头
if(j > n) break;
if(i > maxn)
{
int lcp = get1(i, j), lcs = get2(i, j);
maxn = i + lcp - ;
int l = i - lcs + , r = j + lcp - * k;
if(lcp + lcs > k) ++ f[l], -- f[r + ];
}
if(i > maxn2)
{
int lcp = get2(n - i + , n - j + ), lcs = get1(n - i + , n - j + );
maxn2 = i + lcp - ;
int l = i - lcs + , r = j + lcp - * k;
if(lcp + lcs > k) ++ g[l], -- g[r + ];
}
}
}
for(R i = ; i <= n; i ++) f[i] += f[i - ], g[i] += g[i - ];
rev(g + , g + n + );
} void work()
{
ans = ;//f是开头
for(R i = ; i <= n; i += )
ans += 1LL * f[i] * g[i - ] + ((i + > n) ? : 1LL * f[i + ] * g[i]);
printf("%lld\n", ans);
} int main()
{
// freopen("in.in", "r", stdin);
scanf("%d", &T);
while(T --)
{
init();
pre();
get_sa();
build();
build1();//建st(维护LCP)
rev(s + , s + n + );//翻转
// memset(rk, 0, sizeof(rk));//因为对于单组数据而言,长度不变,所以rk不必再次清空
get_sa();
build();
build2();//建st2(维护LCS)
get();
work();
}
// fclose(stdin);
return ;
}

[NOI2016]优秀的拆分 后缀数组的更多相关文章

  1. BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)

    BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...

  2. UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)

    连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...

  3. BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组

    我们只需要统计在某一个点开始的形如$AA$字符串个数,和结束的个数相乘求和. 首先枚举循环节的长度L.即$\mid (A) \mid=L$ 然后肯定会经过s[i]和[i+L]至少两个点. 然后我们可以 ...

  4. [NOI2016]优秀的拆分(SA数组)

    [NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...

  5. UOJ#219. 【NOI2016】优秀的拆分 [后缀数组 ST表]

    #219. [NOI2016]优秀的拆分 题意:求有多少AABB样子的子串,拆分不同的同一个子串算多个 一开始一直想直接求,并不方便 然后看了一眼Claris的题解的第一行就有思路了 如果分开,求\( ...

  6. NOI 2016 优秀的拆分 (后缀数组+差分)

    题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同 作为一道国赛题,95分竟 ...

  7. luogu1117 优秀的拆分 (后缀数组)

    考虑分别计算每个位置作为AA的末尾或者BB的开头的个数 最后乘一乘就是答案 据说是套路的计算AA的方法: 首先枚举A的长度L,然后每L个字符当做一个关键点,这样的话,一个AA包含且只包含相邻两个关键点 ...

  8. [NOI2016]优秀的拆分&&BZOJ2119股市的预测

    [NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...

  9. luogu1117 [NOI2016]优秀的拆分

    luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...

随机推荐

  1. Mysql:查询当天、今天、本周、上周、本月、上月、本季度、本年的数据

    1. 今天 select * from 表名 WHERE TO_DAYS(时间字段名) = TO_DAYS(NOW()); 2. 昨天 3. 本周 SELECT * FROM 表名 WHERE YEA ...

  2. Drupal8 Console 命令行工具

    转载:https://yplam.com/post/79 Drupal Console 是面向Drupal 8 的一套命令行工具,用来生成Drupal 8模板代码,并且可以跟Drupal 8应用进行交 ...

  3. SQL查找重复项目

    1 2 3 4 5 6 7 SELECT t1.* FROM t1,   (SELECT name,ADD    FROM t1    GROUP BY name,ADD HAVING COUNT(1 ...

  4. 身份证扫描识别/身份证OCR识别的正确姿势,你get到了吗?

    自从国家规定电信实名制之后,实名制已经推广到各个领域:办理通信业务需要实名制.银行开户需要实名制.移动支付需要实名制,就连注册个自媒体账户都需要实名制. 而实名制的背后,就是身份证信息的采集和录入验证 ...

  5. redis 为什么快

    redis采用自己实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大. 不过,因为一般的内存操作都是简单存取操作,线程占用时间相对较短,主要问题在io上,因此,redis这种模型是 ...

  6. 【python 3.6】python读取json数据存入MySQL(一)

    整体思路: 1,读取json文件 2,将数据格式化为dict,取出key,创建数据库表头 3,取出dict的value,组装成sql语句,循环执行 4,执行SQL语句 #python 3.6 # -* ...

  7. Python os.makedirs() 方法

    os.makedirs() 方法用于递归创建目录.像 mkdir(), 但创建的所有intermediate-level文件夹需要包含子目录. 语法 makedirs()方法语法格式如下: os.ma ...

  8. 基于C#的机器学习--颜色混合-自组织映射和弹性神经网络

    自组织映射和弹性神经网络 自组织映射(SOM),或者你们可能听说过的Kohonen映射,是自组织神经网络的基本类型之一.自组织的能力提供了对以前不可见的输入数据的适应性.它被理论化为最自然的学习方式之 ...

  9. Machine Learning方法总结

    Kmeans——不断松弛(?我的理解)模拟,将点集分成几堆的算法(堆数需要自己定). 局部加权回归(LWR)——非参数学习算法,不用担心自变量幂次选择.(因此当二次欠拟合, 三次过拟合的时候不妨尝试这 ...

  10. Python3 解压序列

    一 普遍情况: x,y,z = 1,2,3 print("x:",x) # x:1 print("y:",y) # y:2 print("z:&quo ...