zoukankan      html  css  js  c++  java
  • [HAOI2016]找相同字符

    VII.[HAOI2016]找相同字符

    第一道自己做出的SA题祭~~~

    实际上和上一题没啥区别的说……

    我们发现,这题实际上就是对于两个串中所有的后缀求\(\operatorname{LCP}\)之和(因为这两个后缀共有\(\operatorname{LCP}\)个前缀是相同的,即串中有这么多子串是相同的)。

    老套路,俩串中间插个字符怼一起,求个\(ht\)数组。记录个前缀和,\(s1_i\)表示前\(i\)个字符有多少个来自串一,\(s2_i\)表示前\(i\)个字符有多少来自串二,然后仍然跑单调栈,算出所有来自不同串的后缀对数量,乘上\(ht\)求和即可。

    关键代码:

    for(int i=1;i<n;i++)res+=(1ll*(s1[i-1]-s1[L[i]-1])*(s2[R[i]-1]-s2[i-1])+1ll*(s2[i-1]-s2[L[i]-1])*(s1[R[i]-1]-s1[i-1]))*ht[i];
    

    复杂度\(O(n)\),假如你用DC3的话。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,p;
    int x[400100],y[400100],sa[400100],ht[400100],rk[400100],buc[400100];
    int stk[400100],tp,L[400100],R[400100];
    int s1[400100],s2[400100];
    char s[400100];
    ll res;
    bool mat(int a,int b,int k){
    	if(y[a]!=y[b])return false;
    	if((a+k<n)^(b+k<n))return false;
    	if((a+k<n)&&(b+k<n))return y[a+k]==y[b+k];
    	return true;
    }
    void SA(){
    	for(int i=0;i<n;i++)buc[x[i]=s[i]]++;
    	for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    	for(int i=n-1;i>=0;i--)sa[--buc[x[i]]]=i;
    	for(int k=1;k<n;k<<=1){
    		int num=0;
    		for(int i=n-k;i<n;i++)y[num++]=i;
    		for(int i=0;i<n;i++)if(sa[i]>=k)y[num++]=sa[i]-k;
    		for(int i=0;i<=m;i++)buc[i]=0;
    		for(int i=0;i<n;i++)buc[x[y[i]]]++;
    		for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
    		for(int i=n-1;i>=0;i--)sa[--buc[x[y[i]]]]=y[i],y[i]=0;
    		swap(x,y);
    		x[sa[0]]=num=0;
    		for(int i=1;i<n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
    		m=num;
    	}
    	for(int i=0;i<n;i++)rk[sa[i]]=i;
    	for(int i=0,k=0;i<n;i++){
    		if(!rk[i])continue;
    		if(k)k--;
    		int j=sa[rk[i]-1];
    		while(i+k<n&&j+k<n&&s[i+k]==s[j+k])k++;
    		ht[rk[i]]=k;
    	}
    }
    int main(){
    	scanf("%s",s),p=strlen(s),s[p]=' ',scanf("%s",s+p+1),n=strlen(s),m='z';
    //	printf("%s\n",s);
    	SA();
    	for(int i=1;i<n;i++)s1[i]=s1[i-1]+(sa[i]<p),s2[i]=s2[i-1]+(sa[i]>p);
    //	for(int i=0;i<n;i++)printf("%d ",sa[i]);puts("");
    //	for(int i=0;i<n;i++)printf("(%d,%d)",s1[i],s2[i]);puts("");
    	for(int i=1;i<n;i++){
    		while(tp&&ht[stk[tp]]>=ht[i])R[stk[tp--]]=i;
    		L[i]=stk[tp],stk[++tp]=i;
    	}
    //	for(int i=0;i<n;i++)printf("%d ",rk[i]);puts("");
    	while(tp)R[stk[tp--]]=n;
    //	for(int i=1;i<n;i++)printf("%d:(%d,%d):%d\n",i,L[i],R[i],ht[i]);
    	for(int i=1;i<n;i++)res+=(1ll*(s1[i-1]-s1[L[i]-1])*(s2[R[i]-1]-s2[i-1])+1ll*(s2[i-1]-s2[L[i]-1])*(s1[R[i]-1]-s1[i-1]))*ht[i];
    	printf("%lld\n",res);
    	return 0;
    }
    

  • 相关阅读:
    以用户名注册来分析三种Action获取数据的方式
    Struts2中的OGNL详解 《转》
    Module 'null' not found异常解决办法
    struts标签<logic:iterate>的用法
    struts2的核心和工作原理 <转>
    jstl标签怎么实现分页中下一页
    forward 和redirect
    forward 和redirect的区别
    今天早上起来就想着要问问龙虎有圆通没
    昨天晚上回来弄了两个皮蛋吃
  • 原文地址:https://www.cnblogs.com/Troverld/p/14602373.html
Copyright © 2011-2022 走看看