SPOJ Substrings 题解
首先我们需要知道几个关于
结论 1:题目中的
单调下降。
显然,对于长度为
结论 2:两个
集合要么是包含关系,要么是非交的,且取决于一个字符串是否是另一个字符串的后缀。
如果字符串
否则,两者结尾不同有字符,故对于
结论 3:一个节点有一个终点当且仅当它的子树包含这个终点对应的主链的节点,一个子串在字符串中的出现次数为其子树中主链的节点的数量。
在这里,主链表示字符串前缀所在的终点等价类所代表的节点。
每个终点都有一个对应的主链的节点。
必要性:如果这个节点包含这个终点,根据结论 2,其必然包含主链上节点的
充分性:如果这个节点包含这个终点,但是不包含主链上的对应节点,则两者集合有交,与结论 2 矛盾,所以如果这个节点包含这个终点则必然包含主链上的对应节点。
那么知道了这些结论,我们该怎么做题呢?
发现了没有,对于一个终点等价类的子串,它们显然是有共同的出现次数的,而这一次数可以利用结论 4 求出。
所以我们考虑用一个拓扑来做一个树形 DP,求出之后,我们在节点的
为什么可以直接后缀最大值呢?参见结论 1。
#include<bits/stdc++.h> #define LL long long using namespace std; const LL N=1e6+5; struct node { LL len,fa,num,du; map<char,LL>nxt; }t[N]; LL n,a[N],lst,sz,f[N]; char c[N]; queue<LL>q; void ins(char x) { LL now=++sz; t[now].len=t[lst].len+1; t[now].num=1; LL p=lst; lst=now; while(p!=-1&&!t[p].nxt.count(x)) { t[p].nxt[x]=now; p=t[p].fa; } if(p==-1)return; LL q=t[p].nxt[x]; if(t[p].len+1==t[q].len) { t[now].fa=q; return; } LL cl=++sz; t[cl]=t[q],t[cl].num=0,t[cl].len=t[p].len+1; while(p!=-1&&t[p].nxt[x]==q) { t[p].nxt[x]=cl; p=t[p].fa; } t[q].fa=t[now].fa=cl; } vector<char>v; int main() { t[0].fa=-1; scanf("%s",c+1); n=strlen(c+1); for(int i=1;i<=n;i++)ins(c[i]); for(int i=1;i<=sz;i++) { t[t[i].fa].du++; } for(int i=1;i<=sz;i++) { if(!t[i].du)q.push(i); } while(!q.empty()) { LL k=q.front(); q.pop(); t[t[k].fa].num+=t[k].num; t[t[k].fa].du--; if(!t[t[k].fa].du) { q.push(t[k].fa); } } for(int i=1;i<=sz;i++) { f[t[i].len]=max(f[t[i].len],t[i].num); } for(int i=n-1;i>=1;i--) { f[i]=max(f[i],f[i+1]); } for(int i=1;i<=n;i++) { printf("%lld\n",f[i]); } return 0; }
如果觉得不错的话,就给一个赞吧!
作者是 DengDuck ,转载请注明出处
文章链接: https://www.cnblogs.com/dengduck/p/17527972.html
感谢您阅读!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步