• 【JZOJ6213】【20190613】String


    题目

    img

    (n le 10^{18} , |T| le 10^5)

    题解

    • 显然,最少的操作次数一定是贪心地能匹配就匹配

    • 我们可以建出(T)的SAM,把SAM不能走的边补到根的后继节点

    • 问题变成在SAM上设计一条长度为(n)的路径使得它绕回根的次数最多

    • 由于字符集为4,并且我们只关心绕回根的次数

    • 可以在SAM上dp预处理一个(4 imes 4)的矩阵表示从(x)开头经过根到(y)开头的最小长度

    • 二分答案,矩阵快速幂check

    • 考场没有想到二分,直接搜索了一下ABCD的顺序然后重复走中间的环,水过去了。。

      #include<bits/stdc++.h>
      #define ll long long 
      #define inf 1e9
      
      using namespace std;
      
      const int N=200010;
      int l,cnt,ch[N][4],fa[N],len[N],d[N],a[10],used[N];
      char s[N];
      ll n,ans=1,f[N],dis[4][4];
      
      void chkmn(ll&x,ll y){if(x>y)x=y;}
      void chkmx(ll&x,ll y){if(x<y)x=y;}
      
      void pre_dfs(int u){
      	if(used[u])return;
      	used[u]=1;
      	for(int i=0;i<4;++i){
      		int v=ch[u][i];
      		if(!v)continue;
      		d[v]++;
      		pre_dfs(v);
      	}
      }
      
      void get_dis(int I){
      	static queue<int>q;
      	q.push(ch[1][I]);
      	f[ch[1][I]]=1;
      	while(!q.empty()){
      		int u=q.front();q.pop();
      		for(int i=0;i<4;++i){
      			int v=ch[u][i];
      			if(!v){chkmn(dis[I][i],f[u]);continue;}
      			chkmn(f[v],f[u]+1);
      			if(!--d[v])q.push(v);
      		}
      	}
      }
      
      void get_ans(int m){
      	static int vis[4],s1,s2,s3,st[10],tp;
      	for(int i=s1=s2=s3=0;i<4;++i)vis[i]=0;
      	vis[st[tp=1]=a[0]]=1;
      	for(int i=1;i<m;++i){
      		s1+=dis[a[i-1]][a[i]];
      		if(vis[a[i]]){
      			int lst=a[i];
      			while(1){
      				s2+=dis[st[tp]][lst];s3++;
      				if(st[tp]==a[i])break;
      				vis[st[tp]]=0;
      				lst=st[tp--];
      			}
      			continue;
      		}
      		vis[st[++tp]=a[i]]=1;
      	}
      	if(n<=s1)return;
      	chkmx(ans, m);
      	if(s2)chkmx(ans, m+1ll*(n-1-s1)/s2*s3);	
      }
      
      void dfs(int x){
      	if(x>8)return;
      	if(x>1)get_ans(x);
      	for(int i=0;i<4;++i){
      		a[x]=i;
      		dfs(x+1);
      	}
      }
      
      int main(){
      	freopen("string.in","r",stdin);
      	freopen("string.out","w",stdout);
      	scanf("%lld%s",&n,s);l=strlen(s);
      	cnt=1;
      	for(int i=0,lst=1;i<l;++i){
      		int x=s[i]-'A',p=lst,np=++cnt;
      		len[lst=np]=len[p]+1;
      		while(p&&!ch[p][x])ch[p][x]=np,p=fa[p];
      		if(!p){fa[np]=1;continue;}
      		int q=ch[p][x];
      		if(len[q]==len[p]+1)fa[np]=q;
      		else{
      			int nq=++cnt;
      			len[nq]=len[p]+1;
      			memcpy(ch[nq],ch[q],sizeof(ch[q]));
      			fa[nq]=fa[q];fa[np]=fa[q]=nq;
      			while(p&&ch[p][x]==q)ch[p][x]=nq,p=fa[p];
      		}
      	}
      	
      	for(int i=0;i<4;++i)if(!ch[1][i]){return cout<<n<<endl,0;}
      	memset(dis,0x3f,sizeof(dis));
      	memset(f,0x3f,sizeof(f));
      	
      	for(int i=0;i<4;++i){
      		for(int j=1;j<=cnt;++j)d[j]=used[j]=0;
      		pre_dfs(ch[1][i]);
      		get_dis(i);
      	}
      	
      	dfs(0);
      
      	cout<<ans<<endl;
      
      	return 0;
      }
      
  • 相关阅读:
    第九章 监控系统zabbix深入应用监控
    二叉树的迭代遍历
    .NET程序设计实验2
    大数据分析——sklearn模块安装
    C#实现找二维数组中的鞍点
    Javascript——DOM简介
    前端基础——HTML(一)
    Java基础(一)
    前端基础——CSS(一)
    如何配置网络,使内网和WiFi同时使用
  • 原文地址:https://www.cnblogs.com/Paul-Guderian/p/11074664.html
Copyright © 2020-2023  润新知