读题发现,只要保证每个字符串加入的时候它的后缀都加入了,那么总代价最大是n*(n-1)/2,所以不会出现第一种情况。
建出一棵树,每个点的father是它最长的后缀,然后贪心每次走较小的子树就可以。
找后缀可以字符串反序从短到长建trie
注意在压字符串的时候,字符串大小要开总长度+字符串个数。
#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<vector>#define ll long long#define inf 1e9#define eps 1e-8#define md#define N 100010#define M 510010using namespace std;struct yts { int x,t,ne;} e[N];int ch[M][26],id[M];vector<int> vec[N];char st[1000010];int q[N],c[M],len[N],sz[N],v[N],pos[N],fr[N];int cnt=1,n,mxlen,num,dfs_cnt;ll ans=0;void put(int x,int y){num++; e[num].x=x; e[num].t=y;e[num].ne=v[x]; v[x]=num;}void sort_len(){for (int i=1;i<=n;i++) c[len[i]]++;for (int i=1;i<=mxlen;i++) c[i]+=c[i-1];for (int i=n;i;i--) q[c[len[i]]--]=i;}int insert(int ID,char st[]){int x=1,father=0;for (int i=len[ID]-1;i>=0;i--){int c=st[i]-'a';if (!ch[x][c]) ch[x][c]=++cnt;x=ch[x][c]; if (id[x]) father=id[x];}id[x]=ID;return father;}void dfs1(int x){sz[x]=1;for (int i=v[x];i;i=e[i].ne){int y=e[i].t;dfs1(y);sz[x]+=sz[y];}}bool cmp (int x,int y) { return sz[x]<sz[y];}void dfs2(int x,int fa){pos[x]=dfs_cnt++; ans+=pos[x]-pos[fa];if (!v[x]) return;int w=0;for (int i=v[x];i;i=e[i].ne) q[++w]=e[i].t;sort(q+1,q+w+1,cmp);for (int i=1;i<=w;i++) vec[x].push_back(q[i]);for (int i=0;i<w;i++) dfs2(vec[x][i],x);}int main(){scanf("%d",&n);int sum=0;for (int i=1;i<=n;i++){scanf("%s",st+sum);fr[i]=sum; len[i]=strlen(st+sum);sum+=len[i]+1;mxlen=max(mxlen,len[i]);}sort_len();for (int i=1;i<=n;i++) put(insert(q[i],st+fr[q[i]]),q[i]);dfs1(0); dfs2(0,0);printf("%lld\n",ans);return 0;}
本文介绍了一种利用字符串后缀树和贪心策略解决特定字符串问题的方法。通过构建后缀树并使用贪心算法,确保了每次操作选择最优路径,避免了不必要的重复计算。文章还详细介绍了如何反向构建Trie树来查找字符串的后缀。
643

被折叠的 条评论
为什么被折叠?



