分析:
其实题目就是要求任意两个后缀T[i]和T[j] (i< j) 的 LCP长度之和。
首先对输入的字符串反转后建立SAM。
令 一个节点的Max表示它代表的最长子串。
推论1: 原串中的两个后缀的LCP长度等于后缀树上两个节点的LCA 的 Max.
基于这个推论,只需要枚举LCA,讨论子树与子树之间的组合数问题。
虽然理论上要建立后缀树,但代码里并不用真的建一棵树再DFS。
具体做法如下:
推论2: Max更大的节点一定在后缀树中深度更大
根据这个推论,我们可以按照Max从小到大进行排序,得到一个序列dfn[]就是深度从浅到深的节点编号,可以进行DP。
考虑到1<=Max<=n, 代码中用的是O(n)的基数排序。
注意: DP中累加size的时候,建立的辅助节点nq没有算在其中。

代码:

/**************************************************************Problem: 3238User: sparkLanguage: C++Result: AcceptedTime:3072 msMemory:244452 kb
****************************************************************/#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define LL long long
using namespace std;
const int maxn=1000000+5;int tot=1,last=1,n,dfn[maxn],cnt[maxn];
char s[maxn];struct node{int Next[26];int Max,size,pre,sons;
}T[maxn<<1];template <typename T>
inline void _read(T &x){char ch=getchar(); bool mark=false;for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';if(mark)x=-x;
}void Insert(char x){int id= x-'a';int np= ++tot,cur=last;T[np].Max=T[last].Max+1; T[np].size=T[np].sons=1;while(cur){if(!T[cur].Next[id])  T[cur].Next[id]= np;else {int v= T[cur].Next[id];if(T[v].Max==T[cur].Max+1) T[np].pre=v;else{int nq= ++tot;memcpy(T[nq].Next,T[v].Next,sizeof(T[v].Next));T[nq].Max=T[cur].Max+1;T[nq].pre=T[v].pre; T[v].pre= nq; T[np].pre= nq; for(int i= cur;T[i].Next[id]==v;i=T[i].pre)T[i].Next[id]=nq;} break;}cur=T[cur].pre;}if(!T[np].pre)T[np].pre=1;last=np;
}LL Count(){int i;LL ans=0;for(i=1;i<=tot;i++) cnt[T[i].Max]++;for(i=1;i<=n;i++)cnt[i]+=cnt[i-1];for(i=1;i<=tot;i++)dfn[cnt[T[i].Max]--]= i;// cout<<"dfn: ";for(i=1;i<=tot;i++)cout<<dfn[i]<<" ";cout<<endl;for(i=tot;i>0;i--)T[T[dfn[i]].pre].size+=T[dfn[i]].size;for(i=1;i<=tot;i++){int fa= T[i].pre;ans+= 1ll*T[fa].sons*T[fa].Max*T[i].size;T[fa].sons+= T[i].size;}return ans;
}int main(){int i,j;scanf("%s",s+1);n= strlen(s+1); for(i=n;i>0;i--)Insert(s[i]);LL ans=0;for(i=1;i<=n;i++) ans+= (1ll*i*(n-1));cout<<ans-2*Count()<<endl;return 0;
}
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define Marx_is_dead true
#define ll long long
using namespace std;
template <typename T>
inline void _read(T& x){char t=getchar();bool sign=true;while(t<'0'||t>'9'){if(t=='-')sign=false;t=getchar();}for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';if(!sign)x=-x;
}
int n,m;
int last=1,root=1,tot=1;
struct node{int son[26];int maxn,size,par,temp;
};
node Auto[2000005];
char s[1000005];
void insert(char ch){int i,j,k,nq,p,q,t=ch-'a';int np=++tot;Auto[np].size=1;Auto[np].temp=1;Auto[np].maxn=Auto[last].maxn+1;for(p=last;!Auto[p].son[t];p=Auto[p].par)Auto[p].son[t]=np;if(!p)Auto[np].par=root;else {q=Auto[p].son[t];if(Auto[q].maxn!=Auto[p].maxn+1){nq=++tot;memcpy(Auto[nq].son,Auto[q].son,sizeof(Auto[q].son));//Auto[nq]=Auto[q];Auto[nq].maxn=Auto[p].maxn+1;Auto[nq].par=Auto[q].par;Auto[q].par=nq;Auto[np].par=nq;for(;Auto[p].son[t]==q;p=Auto[p].par)Auto[p].son[t]=nq;}else Auto[np].par=q;}if(Auto[np].par==0)Auto[np].par=root;last=np;
}
int cnt[1000005];
int dfn[1000005];
int main(){int i,j,len;scanf("%s",s+1);len=strlen(s+1);for(i=len;i;i--){insert(s[i]);}ll ans=1ll*(len+1)*(len-1)*len/2;for(i=1;i<=tot;i++)cnt[Auto[i].maxn]++;for(i=1;i<=len;i++)cnt[i]+=cnt[i-1];for(i=1;i<=tot;i++)dfn[cnt[Auto[i].maxn]--]=i;for(i=tot;i;i--){Auto[Auto[dfn[i]].par].size+=Auto[dfn[i]].size;}ll dec=0;for(i=1;i<=tot;i++){int fa=Auto[i].par;dec+=1ll*Auto[fa].temp*Auto[i].size*Auto[fa].maxn;Auto[fa].temp+=Auto[i].size;}cout<<ans-2*dec;
}

后缀自动机+DP BZOJ 3238 差异相关推荐

  1. BZOJ 3238 差异 [后缀自动机]

    题目 一个长度为n的字符串S,令TiT_i表示它从第i个字符开始的后缀.求 ∑1≤i<j≤nlen(Ti)+len(Tj)−2∗lcp(Ti,Tj) \sum_{1\le i 其中len(a)表 ...

  2. hdu5343 后缀自动机+dp

    给定两个串,分别截取字串X和Y,连接组成X+Y,求不同的X+Y的方案数. 对于X+Y,如果重复的部分其实就是从同一个X+Y的某个地方断开弄成不同的X和Y,那么只要使得X和X+Y匹配得最长就行了. 因此 ...

  3. BZOJ2806(后缀自动机+DP)

    题目:http://61.187.179.132/JudgeOnline/problem.php?id=2806 题意:给定一个由M个01串组成的字典.依据这个字典和一个阀值L,可以断言一个01串是否 ...

  4. bzoj 3238 差异

    给出一个长n的字符集为小写字母的字符串,求∑1≤i<j≤nlen(Suffixi)+len(Suffixj)−2×len(lcp(Suffixi,Suffixj))\sum \limits _{ ...

  5. Cool Slogans[CF700E][后缀自动机][Dp]

    文章目录 题目 思路 代码 题目 Luogu n ≤ 2 ⋅ 1 0 5 n\le 2\cdot 10^5 n≤2⋅105 思路 性质: ∃ s i \exist\quad s_i ∃si​ 是 s ...

  6. 洛谷P3975【天津省选2015】(后缀自动机DP)

    题目链接 https://www.luogu.com.cn/problem/P3975 题解 此题非常经典且重要,是sam的函谷关,必须拿下. 记录每个点endpos大小的方法是在parent树从下往 ...

  7. BZOJ-3473 (广义后缀自动机:拓扑 or 启发式合并)

    BZOJ-3473 (广义后缀自动机:拓扑 or 启发式合并) 题目链接 题意 nnn个字符串,询问每个字符串一共有几个子串至少出现在nnn个字符串中的kkk个 思路: 拓扑 建广义后缀自动机, dp ...

  8. SPOJ 7258 (后缀自动机)

    转载:http://hzwer.com/4492.html 给一个长度不超过90000的串S,每次询问它的所有不同子串中,字典序第K小的,询问不超过500个. 搞出后缀自动机 dp处理出每个点往下走能 ...

  9. BZOJ 2806 Luogu P4022 [CTSC2012]Cheat (广义后缀自动机、DP、二分、单调队列)

    题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2806 (luogu) https://www.luogu.org/pro ...

最新文章

  1. springmv的执行流程是什么
  2. SpringBoot 数据处理
  3. Model层视频播放关闭问题及手机视频播放的适配问题解决方案
  4. CF Gym102059 H. Fractions
  5. Android fillViewPort属性用法
  6. Linux操作系统使用基础04:文件与目录管理
  7. 程序win10_只需3步!教你打造精简win10,去除系统自带程序,运行更快!
  8. 电话机器人源码部署原理及技术难点
  9. laravel 入门指南
  10. Typecho独立下载插件安装与使用
  11. 中兴java笔试题_中兴Java 笔试题.doc
  12. PuTTY使用复制粘贴
  13. Android Studio 连接夜神模拟器的方法
  14. 调查问卷的人口学基本信息该如何设计?
  15. 手把手教你自制555芯片
  16. 日媒:争夺中国人才,跨国公司败北
  17. faststone capture使用长截图(滚动截图)功能教程
  18. oracle导入提示字符过长,Oracle 解决【ORA-01704:字符串文字太长】
  19. liunx常驻运行项目命令
  20. 淘客外卖返利系统,外卖cps系统 ,社群团购系统

热门文章

  1. :[转贴]变态级JAVA程序员面试32问(附答案
  2. c++中按位取反运算
  3. 关于动态链接库和静态链接库
  4. 1 python编程基础学习
  5. oracle通信通道的文件结尾_如何解决ORA-03113: 通信通道的文件结尾?
  6. 嵌入式主板的应用领域
  7. 在c语言中while与do-while,C语言中while /do while语句用法
  8. Jackson JsonNode和ObjectNode的使用
  9. 基存储卡的音乐播放器v0.2
  10. PostgreSQL扫描方法综述