zoukankan      html  css  js  c++  java
  • P6793 [SNOI2020]字符串(后缀树上DP)

    P6793 [SNOI2020]字符串

    给出两个长度为\(n\)的字符串\(a,b\),取出他们所有长为\(k\)的子串(各有\(n-k+1\)个),这些子串分别组成集合\(A,B\)

    现在要修改\(A\)中的串,使得\(A\)\(B\)完全相同。

    可以任意次选择修改\(A\)中的一个串的一段后缀。花费为这段后缀的长度。

    总花费为每次修改花费之和。

    求总花费的最小值。

    做法:

    \(a\)\(b\)的后\(n-k+1\)个字符倒序插入SAM。

    然后建出link树,\(a\)\(b\)的后缀在树上对应若干个节点。

    然后对于两个节点,他们的lca越深匹配效果越好。

    因为匹配代价是\(k-len(lca)\)

    问题转化为:

    给出树上两个相同的点集。

    两个点匹配的代价是\(min(k,dep(lca))\)

    询问怎么匹配,总代价最小。

    对每个子树维护两个值:

    \(f[u][0]\)表示点集\(A\)剩余未匹配的点数。

    \(f[u][1]\)表示点集\(B\)剩余未匹配的点数。

    自底向上转移即可。

    时间复杂度\(O(n)\)

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+10;
    int n,k,nxt[maxn][26],link[maxn],len[maxn],dp[maxn][2],tot=1,lst=1;
    string A,B;
    void sam_extend (char c,int f,int v) {
    	int cur=++tot,p=lst;
    	dp[cur][f]=v;
    	len[cur]=len[lst]+1;
    	while (p&&!nxt[p][c-'a']) {
    		nxt[p][c-'a']=cur;
    		p=link[p];
    	}
    	if (!p) link[cur]=1;
    	else {
    		int q=nxt[p][c-'a'];
    		if (len[p]+1==len[q]) link[cur]=q;
    		else {
    			int clone=++tot;
    			len[clone]=len[p]+1;
    			for (int i=0;i<26;i++) nxt[clone][i]=nxt[q][i];
    			link[clone]=link[q];
    			while (p&&nxt[p][c-'a']==q) {
    				nxt[p][c-'a']=clone;
    				p=link[p];
    			}
    			link[q]=link[cur]=clone;
    		}
    	}
    	lst=cur;
    }
    vector<int> g[maxn];
    long long ans=0;
    void dfs (int u) {
    	for (int v:g[u]) {
    		dfs(v);
    		for (int i=0;i<2;i++) dp[u][i]+=dp[v][i];
    	}
    	int gg=min(dp[u][0],dp[u][1]);
    	ans+=1ll*gg*max(0,k-len[u]);
    	dp[u][0]-=gg;
    	dp[u][1]-=gg;
    }
    int main () {
    	ios::sync_with_stdio(false);
    	cin>>n>>k;
    	cin>>A;
    	cin>>B;
    	for (int i=n-1;i>=0;i--) {
    		int ff=0;
    		if (i<=n-k) ff=1;
    		sam_extend(A[i],0,ff);
    	}
    	lst=1;
    	for (int i=n-1;i>=0;i--) {
    		int ff=0;
    		if (i<=n-k) ff=1;
    		sam_extend(B[i],1,ff); 
    	}
    	for (int i=2;i<=tot;i++) {
    		g[link[i]].push_back(i);
    	}
    	dfs(1);
    	cout<<ans;
    }
  • 相关阅读:
    《实战Java高并发程序设计》读书笔记一
    《实战Java高并发程序设计》读书笔记二
    SprintBoot学习(三)
    SprintBoot学习(二)
    SprintBoot学习(一)
    jQuery学习(三)
    jQuery学习(二)
    jQuery学习(一)
    利用activeX控件在网页里自动登录WIN2003远程桌面并实时控制
    上传读取Excel文件数据
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15516160.html
Copyright © 2011-2022 走看看