前言:虽然这题前面加了个括号是“省选模拟30”,但是在accoders上是比赛“省选模拟31”里面的。

题目描述

题解

先贴出官方正解,是用的和后缀数组:

根据“万串皆可后缀机”的套路,这题我还是选择用后缀自动机(SAM)做。容易发现一个串的最小表示包含的信息等价于每个位置记录它前面第一个与它相同的字符出现位置的距离,比如“reverse”和“abcbadb”都可以表示为“0,0,0,2,4,0,3”(为了方便,直接记这种表示为“最小表示”)。所以可以得到一个做法,把所有子串插入广义SAM里面(自动去重),然后遍历一遍统计答案。

接下来,问题变为怎么尽可能少地插入子串,也就是让插入的串尽可能多地包含其它子串。因为一个子串相当于原串掐头去尾,而前面去掉的部分会影响后面的“最小表示”,所以我们考虑从后往前维护每一个后缀的最小表示,具体就是记录每一种字符在后面出现的第一个位置,与当前字符比较,然后更新后面的“最小表示”。

假设当前维护到第 i 个字符为 c,分两种情况:

  1. i 后面没有出现 c,那么除了 i 处的最小表示为 0 之外,其它位置不变,不会影响 i+1~n 的最小表示;
  2. i 后面出现的第一个 c 位置为 j ,那么除了 i 处最小表示为 0 、j 处最小表示为 j-i 之外,其它位置不变,不会影响 i+1~j-1 和 j+1~n 的最小表示。

由于子串倒转后统计结果不变,我们可以把最小表示倒着插入,记录每个后缀在SAM上的对应节点为

  • 那么对于第一种情况,从处再插入一个 0 即可;
  • 对于第二种情况,从处倒着插入一遍 i~j 的最小表示即可。

算法可行的关键在于第二种情况的插入次数,因为对于每一种字符,最多使其插入 n 次,所以总共插入不超过 nm 次。

但是做到这里还没完,当我们在第二种情况进行插入时,更改后的 i+1~j 的子串是不存在的,因为当且仅当子串中包含 i 时 j 的最小表示为 j-i 。怎么在统计时去掉这些子串呢?

由于上每个节点的子树存了该节点代表的子串的所有右端点位置,换过来就是插入时我们希望算入的子串的左端点位置,这时我们可以在插入时记录每个端点是否合法(比如在第二种情况中,只有 i 作为左端点时合法),统计时只统计子树中包含合法右端点的节点即可。

由于插入的是数字,故用map储存SAM上的边,总时间复杂度为

代码

评测要带freopen

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#define ll long long
#define uns unsigned
#define MAXN 50005
#define INF 0x3f3f3f3f
using namespace std;
inline ll read(){ll x=0;bool f=1;char s=getchar();while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();return f?x:-x;
}//SAM
struct SAM{map<int,int>ch;int len,fa;SAM(){len=fa=0,ch.clear();}
}sam[MAXN<<5];
int tot=1,la[MAXN],num[MAXN<<5];
inline bool fd(int id,int c,int x){return sam[id].ch.find(c)!=sam[id].ch.end()&&sam[id].ch[c]==x;
}
inline int samadd(int c,int las,int ad){int p=las,np=las=++tot;sam[np].len=sam[p].len+1;for(;p&&sam[p].ch[c]==0;p=sam[p].fa)sam[p].ch[c]=np;if(!p)sam[np].fa=1;else{int q=sam[p].ch[c];if(sam[q].len==sam[p].len+1)sam[np].fa=q;else{int nq=++tot;sam[nq]=sam[q],sam[nq].len=sam[p].len+1,sam[q].fa=sam[np].fa=nq;for(;p&&fd(p,c,q);p=sam[p].fa)sam[p].ch[c]=nq;}}num[np]+=ad;return np;
}//main
int n,m,si[15],id[MAXN];
ll ans;
char s[MAXN];
vector<int>G[MAXN<<5];
inline int dfs(int x){int nu=num[x];for(uns i=0;i<G[x].size();i++)nu+=dfs(G[x][i]);if(x>1&&nu>0)ans+=sam[x].len-sam[sam[x].fa].len;return nu;
}
signed main()
{freopen("string.in","r",stdin);freopen("string.out","w",stdout);n=read(),m=read();scanf("%s",s+1),la[n+1]=1;for(int i=n;i>0;i--){if(si[s[i]-'a']){int k=si[s[i]-'a'];id[k]=k-i;for(int j=k;j>=i;j--)la[j]=samadd(id[j],la[j+1],j==i);}else la[i]=samadd(id[i],la[i+1],1);si[s[i]-'a']=i;}for(int i=2;i<=tot;i++)G[sam[i].fa].push_back(i);dfs(1);printf("%lld\n",ans);return 0;
}

由于多数人用的是没被卡掉的空间小的,我的空间显得格外突兀:

[2021.4.5多校省选模拟30]最小表示——map建边+广义SAM相关推荐

  1. [2021.1.17多校省选模拟4]T1(莫比乌斯反演/组合数学/枚举倍数)

    [2021.1.17多校省选模拟4]T1 一般人都会想着去枚举直线的斜率,但是枚举斜率之后就会产生多条直线,并且这些直线的长度不一,难以快速求解,所以我们考虑换一种方法枚举. 枚举最远点对的横纵坐标之 ...

  2. [2021.1.13多校省选模拟2]T1(动态规划/轮廓线dp)

    [2021.1.13多校省选模拟2]T1 一个经典的轮廓线dp,可以发现一定可以找到一条轮廓将这个图形分开,然后使得左半部分由左边处理,右半部分由右边处理,然后我们只需要处理这个折线即可,具体实现需要 ...

  3. [2021.1.31多校省选模拟12]随机变换的子串(线段树维护分治/字符串/自动机思想)

    [2021.1.31多校省选模拟12]随机变换的子串 对于这三种操作,我们惊奇地发现有这样的性质,所有长度大于4的字符串都可以通过变换变为长度小于等于4的字符串,那么查询本质不同的字符串我们只需要处理 ...

  4. A. [2021.1.29多校省选模拟11]最大公约数(杜教筛/数论)

    A. [2021.1.29多校省选模拟11]最大公约数 这是一个杜教筛的经典题目,最后我们只需要筛一下1∗xμ(x)1*x\mu(x)1∗xμ(x)这个函数的前缀和即可,然后看到有111这个函数,我们 ...

  5. [2021.1.27多校省选模拟10]跑步(线段树合并)

    [2021.1.27多校省选模拟10]跑步 经典的树上启发式合并题目,维护对应子树的从当前点到子树内一个节点这个链待定,其他部分已经确定的方案数,这个东西按照对应点到根节点的路径点权和为下标存在一个权 ...

  6. [2021.1.27多校省选模拟10]染色(min-max容斥/二项式反演)

    [2021.1.27多校省选模拟10]染色 突然发现我对概率期望的理解不是很好... 部分分1:可以直接进行状压dp,然后按照题意模拟即可. 部分分2:首先可以发现这个问题是min_max容斥形式,然 ...

  7. [2021.4.7多校省选模拟33]A,B,C

    文章目录 考试复盘 A B C 考试复盘 今天的题其实蛮温柔的 考完试预估分160160160,好家伙到手的只有14\frac{1}{4}41​ 第一题是原题,做过的,虽然忘记怎么做了...⊙︿⊙ 但 ...

  8. 2021-4-1 多校省选模拟赛

    文章目录 考试复盘 nmd 考试复盘 T1T1T1 我可太喜欢这种不要脑子的莽试题了!! 考场上猜的结论d>3d>3d>3无解 d=1,d=2d=1,d=2d=1,d=2的填法也试出 ...

  9. 多校冲刺NOIP模拟6 - 游戏——矩阵乘法、后缀自动机SAM

    此题不提供链接 题目描述 前言 好久没用SAM了.我记得上次用SAM做题还是在上次. 题解 每一种长度的总方案是确定的,所以我们只需要求出赢的方案数.平局方案数即可. 做法其实和官方正解区别不大,官方 ...

最新文章

  1. 灵活运用分布式锁解决数据重复插入问题
  2. Linux下实现文件双向同步
  3. Geoserver怎样切割离线瓦片地图
  4. 利用阿里云自定义镜像实现服务器数据/网站快速迁移
  5. 【TensorFlow-windows】keras接口——利用tensorflow的方法加载数据
  6. 最有创意的万圣节借势海报都在这里
  7. 说出java中基本数据类型_java中的基本数据类型
  8. 退休前后,工资差距有多大?
  9. QList (链表) QVector (数组)
  10. HDFS完全分布式集群搭建
  11. (一)SVM的八股简介
  12. 遍历数组的两种for循环方式以及一种Arrays方式
  13. 前端实现video播放
  14. Linux服务器如何修改登录用户的密码
  15. FPGA中的AXI总线知识点快速学习(适合新手)
  16. css rgb转16进制,jquery获取元素颜色css('color')的值返回RGB。如何转十六进制
  17. 【scratch高阶案例教学】scratch黄金矿工 scratch创意编程 少儿编程 手把手教小朋友们从零制作黄金矿工游戏
  18. FreeRTOS个人笔记-初谈CM3内核
  19. 1919获阿里20亿投资,独角兽如何搅动酒饮业格局
  20. 视频编辑,如何截取视频片段

热门文章

  1. 多专家模型(mixture of experts)
  2. 一次对K8S集群service的“非主流”访问方式引发的网络探究
  3. matlab外罚函数实验报告,优化之外罚函数法(示例代码)
  4. js函数求n个数中的最大值
  5. 导出excel文件时,出现nginx504错
  6. 用户查询意图检测(CIKM Competition数据挖掘竞赛夺冠算法陈运文)
  7. USB2.0电气层信号的表示及速率的识别过程
  8. Apache Drill源码分析(3)--DrilBit以及物理计划
  9. 【码农学编曲】血清合成器Xfer Serum
  10. Carson带你学设计模式:模板方法模式(Template Method)