字符串作业(下)

完全做不动啊啊啊啊啊啊啊

CF590E Birthday

给定 n n n 个仅包含 a , b a,b a,b 的字符串。
你需要去掉尽可能少的字符串,使得剩下的字符串中不存在某一个串是另一个串的子串。

A C AC AC自动机上路径压缩求出 D A G DAG DAG
再传递闭包后求最长反链。
拆点构图后求出最小链覆盖和方案。
通过最小链覆盖构造出最长反链。

A C   C o d e \mathcal AC \ Code AC Code

#include
#define maxn 755
#define maxc 2
#define maxp 20000007
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

int n;
char s[maxp],*S[maxn];
int tr[maxp][maxc] , len[maxn] , fa[maxp] , tag[maxp] , tot;
int q[maxp],L,R;
int c[maxn][maxn],cx[maxn],cy[maxn],ca[maxn];
int vis[maxn],tim;

int dfs(int u){
     
	rep(i,1,n) if(i!=u && c[u][i] && vis[i]!=tim){
     
		vis[i] = tim;
		if(!cy[i] || dfs(cy[i])){
     
			cy[i] = u , cx[u] = i;
			return 1;
		}
	}  
	return 0;
}

vector<vector<int> >Path;

void ser(int u,vector<int>&P){
     
	P.push_back(u);
	if(!cy[u]) return;
	ser(cy[u],P);
}

int main(){
     
	scanf("%d",&n);
	tot = 1;
	rep(i,1,n){
     
		scanf("%s",s);
		int L = strlen(s) , u = 1;
		S[i] = new char[L + 2];
		len[i] = L;
		memcpy(S[i],s,sizeof(char) * L);
		fa[u] = 0;
		rep(i,0,L-1){
     
			int v = s[i] - 'a';
			if(!tr[u][v]) tr[u][v] = ++tot;
			u = tr[u][v];
		}
		tag[u] = i;
	}
	L=0,R=-1;
	rep(i,0,1) if(tr[1][i]) q[++R] = tr[1][i] , fa[tr[1][i]] = 1;
		else tr[1][i] = 1;
	for(int u;L<=R;){
     
		u = q[L++];
		rep(i,0,1) if(tr[u][i]) fa[tr[u][i]] = tr[fa[u]][i] , q[++R] = tr[u][i];
		else tr[u][i] = tr[fa[u]][i];
	}
	rep(i,1,n){
     
		int u=1,v;
		rep(j,0,len[i]-1){
     
			u = tr[u][S[i][j] - 'a'];
			for(v=u;v!=1 && (tag[v] == 0 || tag[v] == i);v=fa[v]);
			c[i][tag[v]] = 1;
			for(int p=u,t;p!=v;p=t)
				t = fa[p] , fa[p] = v;
		}
	}
	rep(k,1,n) rep(i,1,n) rep(j,1,n)
		c[i][j] |= c[i][k] & c[k][j];
	rep(i,1,n) c[i][i] = 0;
	int ans = 0;
	rep(i,1,n) if(!cx[i]) ++tim,ans+=dfs(i);
	printf("%d\n",n-ans);
	rep(i,1,n) if(!cx[i]){
     
		vector<int>r;
		ser(i,r);
		reverse(r.begin(),r.end());
		Path.push_back(r);
	}
	bool flg = 1;
	memset(ca,0,sizeof ca);
	while(flg){
     
		flg = 0;
		rep(i,0,Path.size()-1)
			rep(j,1,n) if(c[Path[i].back()][j] == 1)
				ca[j] = 1;
		rep(i,0,Path.size()-1) while(ca[Path[i].back()]){
     
			flg = 1;
			Path[i].pop_back();
		}
	}
	rep(i,0,Path.size()-1) printf("%d%c",Path[i].back()," \n"[i==Path.size()-1]);
}

CF700E Cool Slogans

给出一个串 S S S
求一个最长字符串序列 { T i } \{T_i\} { Ti}满足所有元素都是 S S S的子串,并且 T i T_i Ti T i − 1 T_{i-1} Ti1中出现了至少两次。

建出后缀自动机。
可以通过反证得到 endpos \texttt{endpos} endpos相同的节点中选长的一定不劣。(劣的话 endpos \texttt{endpos} endpos就不会相同。)
所以就从根往下贪心,能选则选(用线段树合并得到 endpos \texttt{endpos} endpos用于判断),适时回退即可。

#include
#define maxn 400005
#define lim 20
#define maxc 26
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char Start;

int n,m;
char S[maxn],T[maxn];

int fa[maxn],tr[maxn][maxc],len[maxn],rt[maxn],pos[maxn],tot,last;
vector<int>G[maxn];
void ins(int c,bool debug = 0){
     
	if(tr[last][c]){
     
		int p = last , q = tr[last][c];
		if(len[q] != len[p] + 1){
     
			int v=++tot;
			memcpy(tr[v],tr[q],sizeof tr[q]);
			fa[v]=fa[q],len[v]=len[p]+1;
			for(;~p && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
			fa[q] = v;
		}
	}
	else{
     
		int u=++tot,p=last,q;
		len[u]=len[p]+1;
		for(;~p && !tr[p][c];p=fa[p]) tr[p][c]=u;
		if(p==-1) fa[u]=0;
		else if(len[q=tr[p][c]]==len[p]+1) fa[u]=q;
		else{
     
			int v=++tot;
			memcpy(tr[v],tr[q],sizeof tr[q]);
			fa[v]=fa[q],len[v]=len[p]+1;
			for(;~p && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
			fa[q] = fa[u] = v;
		}
	}
}

#define maxp maxn * 30
int lc[maxp],rc[maxp],mx[maxp],mxp[maxp];
void upd(int u){
     
	if(mx[lc[u]] >= mx[rc[u]]) mx[u] = mx[lc[u]] , mxp[u] = mxp[lc[u]];
	else mx[u] = mx[rc[u]] , mxp[u] = mxp[rc[u]];
}
void ins(int &u,int l,int r,int p){
     
	if(!u) u = ++tot , mxp[u] = l;
	if(l==r) return (void)(mx[u]++);
	int m = l+r>>1;
	p <= m ? ins(lc[u],l,m,p) : ins(rc[u],m+1,r,p);
	upd(u);
}
void merge(int &u,int l,int r){
     
	if(!l || !r) return (void)(u=l+r);
	u = ++tot;
	if(lc[l] || rc[l]){
     
		merge(lc[u],lc[l],lc[r]),merge(rc[u],rc[l],rc[r]);
		upd(u);
	}
	else{
     
		mx[u] = mx[l] + mx[r] , mxp[u] = mxp[l];
	}
}
pair<int,int> qry(int u,int l,int r,int ql,int qr){
     
	if(ql>r||l>qr||!u||ql>qr) return make_pair(0,0x3f3f3f3f);
	if(ql<=l&&r<=qr) return make_pair(mx[u],mxp[u]);
	int m = l+r>>1;
	pair<int,int>r1 = qry(lc[u],l,m,ql,qr) , r2 = qry(rc[u],m+1,r,ql,qr);
	if(r1.first >= r2.first) return r1;
	else return r2;
}

int f[maxn],g[maxn],ps[maxn];
void dfs(int u){
     
	for(int v:G[u]){
     
		dfs(v),merge(rt[u],rt[u],rt[v]);
		if(!ps[u]) ps[u] = ps[v];
	}
}
int ans;
void dfs2(int u){
     
	ans = max(ans , f[u]);
	for(int v:G[u]){
     
		if(!u || qry(rt[g[u]],1,n,ps[v]-len[v]+len[g[u]],ps[v]-1).first)
			f[v] = f[u] + 1 , g[v] = v;
		else 
			f[v] = f[u] , g[v] = g[u];
		dfs2(v);
	}
}

char End;

int main(){
     
	
	scanf("%d",&n);
	scanf("%s",S+1);
	fa[0] = -1;
	rep(i,1,n) ins(S[i]-'a') , last = tr[last][S[i] - 'a'] , ps[last] = i;
	rep(i,1,tot){
     
		G[fa[i]].push_back(i);
		if(ps[i]) ins(rt[i] , 1 , n , ps[i]);
	}
	dfs(0);
	dfs2(0);
	printf("%d\n",ans);
}

HDU 5390 tree

树上修改一个点,求一个点到根的值中和当前点的异或最大值。

区间永久化标记打在 t r i e trie trie树上即可。
可个锤子
发现 n log ⁡ 2 n n\log^2n nlog2n M L E MLE MLE
可以把所有操作和询问都离线在线段树的节点上,
分别给每个节点统一处理就可以做到 O ( n log ⁡ n ) O(n\log n) O(nlogn)的内存。
A C   C o d e \mathcal AC\ Code AC Code

#include
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define lc u<<1
#define rc lc|1
#define pb push_back
#define maxp maxn * 16 * 13
using namespace std;

char Start;
int n,m,fa[maxn],st[maxn],ed[maxn],tim,v[maxn],ans[maxn],isa[maxn];
vector<int>G[maxn];
int ch[maxp][2],tot,sz[maxp];
stack<int>bin;

struct opt{
     
	int w,t;
	opt(const int &w=0,const int &t=0):w(w),t(t){
     }
};
vector<opt>Q[maxn<<2];

void dfs(int u){
     
	st[u] = ++tim;
	rep(i,0,G[u].size()-1)
		dfs(G[u][i]);
	ed[u] = tim;
}

void ins(int u,int l,int r,int ql,int qr,int v,int t){
     
	if(l>qr||ql>r||ql>qr)return;
	if(ql<=l&&r<=qr) return (void)(Q[u].pb(opt(v,t)));
	int m = l+r>>1;
	ins(lc,l,m,ql,qr,v,t) , ins(rc,m+1,r,ql,qr,v,t);
}

void qry(int u,int l,int r,int p,int v,int id){
     
	Q[u].pb(opt(-v,id));
	if(l==r) return;
	int m = l+r >> 1;
	p <= m ? qry(lc,l,m,p,v,id) : qry(rc,m+1,r,p,v,id);
}

char End;

int main(){
     
	int T;
	for(scanf("%d",&T);T--;){
     
		scanf("%d%d",&n,&m);
		rep(i,2,n){
     
			scanf("%d",&fa[i]);
			G[fa[i]].push_back(i);
		}
		dfs(1);
		rep(i,1,n) scanf("%d",&v[i]),ins(1,1,n,st[i],ed[i],v[i],1);
		for(int op,u,p,i=1;i<=m;i++){
     
			scanf("%d%d",&op,&u);
			if(op == 0){
     
					isa[i] = 0;
				scanf("%d",&p);
				ins(1,1,n,st[u],ed[u],v[u],-1);
				ins(1,1,n,st[u],ed[u],v[u]=p,1);
			}
			else{
     
				isa[i] = 1;
				qry(1,1,n,st[u],v[u],i);
			}
		}
		
		rep(i,1,n<<2) if(!Q[i].empty()){
     
			rep(i,1,tot) sz[i] = 0 , ch[i][0] = ch[i][1] = 0;
			tot = 1;
			rep(j,0,Q[i].size()-1){
     
				int u = 1;
				if(Q[i][j].w >= 0){
     
					per(d,29,0){
     
						int p = Q[i][j].w >> d & 1;
						if(!ch[u][p]) ch[u][p] = ++tot;
						u = ch[u][p];
						sz[u] += Q[i][j].t;
					}
				}
				else{
     
					Q[i][j].w = -Q[i][j].w;
					int r = 0;
					per(d,29,0){
     
						int p = Q[i][j].w >> d & 1;
						if(sz[ch[u][!p]]) u = ch[u][!p] , r |= 1 << d;
						else u = ch[u][p];
					}
					ans[Q[i][j].t] = max(ans[Q[i][j].t] , r);
				}
			}
			Q[i].clear();
		}
		rep(i,1,m) if(isa[i]) printf("%d\n",ans[i]),ans[i] = 0;
		tim = 0;
		rep(i,1,n) G[i].clear();
	}
}

CF613E Puzzle Lover

给出一个 2 × n 2\times n 2×n的网格,每个格子上有一个字符,再给出一个长度为 k k k的字符串,问不重复经过点的网格游走有多少种方案可以走出这个字符串。

发现只有开头结尾可能会有掉头的走法。
简单哈希简单 d p dp dp即可。
毒瘤去重

#include
#define maxn 4003
#define mod 1000000007
#define md 998244353
#define LL long long
#define S 131
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char s[2][maxn],t[maxn];
int n,K;
LL hs[2][2][maxn],pw[maxn],hst[maxn];
int f[2][2][maxn];

template<class T,class T2>void add(T &u,T2 v){
      
	((u += v) >= mod) && (u -= mod); 
}

LL ans = 0;

void Solve(){
     
	int now = 1 , pre = 0;
	memset(f,0,sizeof f);
	rep(i,1,n){
     
		swap(now,pre);
		memset(f[now],0,sizeof f[now]);
		rep(j,0,1){
     
			if(s[j][i] == t[1]) f[now][j][1]++;
			rep(k,1,min(i,K/2)) if(
				(((hs[j^1][1][i-k+1] - hs[j^1][1][i+1] * pw[k]) % md * pw[k]
				+ (hs[j][0][i] - hs[j][0][i-k] * pw[k])) % md + md) % md
				== 
				hst[2*k]){
     
					f[now][j][2*k]++;
				}
			rep(k,2,K){
     
				if(s[j][i] == t[k]) add(f[now][j][k] , f[pre][j][k-1]);
				if(s[j][i] == t[k] && s[j^1][i] == t[k-1]) add(f[now][j][k] , f[pre][j^1][k-2]);
			}
			rep(k,2,min(K/2,n-i)) if(
				(((hs[j][0][i+k] - hs[j][0][i] * pw[k]) % md * pw[k]
				+(hs[j^1][1][i+1] - hs[j^1][1][i+k+1] * pw[k])) % md + md) % md
				== 
				((hst[K] - hst[K-2*k] * pw[2*k]) % md + md) % md){
     
					add(ans , f[now][j][K-2*k]);
				}
			add(ans,f[now][j][K]);
		}
	}
}

int main(){
     
	scanf("%s%s%s",s[0]+1,s[1]+1,t+1);
	n = strlen(s[0] + 1);
	K = strlen(t + 1);
	pw[0] = 1;
	rep(i,1,2 * max(n,K)) pw[i] = S * pw[i-1] % md;
	rep(t,0,1) rep(i,1,n) hs[t][0][i] = (hs[t][0][i-1] * S + s[t][i]) % md;
	rep(t,0,1) per(i,n,1) hs[t][1][i] = (hs[t][1][i+1] * S + s[t][i]) % md;
	rep(i,1,K) hst[i] = (hst[i-1] * S + t[i]) % md;
	Solve();
	reverse(s[0]+1,s[0]+n+1);
	reverse(s[1]+1,s[1]+n+1);
	rep(t,0,1) rep(i,1,n) hs[t][0][i] = (hs[t][0][i-1] * S + s[t][i]) % md;
	rep(t,0,1) per(i,n,1) hs[t][1][i] = (hs[t][1][i+1] * S + s[t][i]) % md;
	Solve();
	if(K == 1) ans /= 2;
	if(K == 2){
     
		rep(i,1,n){
     
			if(s[0][i] == t[1] && s[1][i] == t[2])
				ans --;
			if(s[0][i] == t[2] && s[1][i] == t[1])
				ans --;
		}
	}
	//if(s[0][1] == 'w' && s[0][2] == 'g' && s[0][3] == 'w' && s[0][4] == 'g'){
     
	//	puts(t+1);
	///}
	printf("%lld\n",ans);
}

HDU 5394 Trie in Tina Town

t r i e trie trie树上建回文树, O ( n log ⁡ n ) O(n\log n) O(nlogn)

C o d e \mathcal Code Code

#pragma comment(linker, "/STACK:102400000,102400000")
#include
#include
#include
#include
#define maxn 2000006
#define LL long long
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
     
	char ch;
	for(;!isdigit(ch=getc()););
	for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
#define maxc 4
int n;
int tr[maxn][maxc],fa[maxn],len[maxn],dif[maxn],anc[maxn],pos[maxn],tot;
LL sl[maxn],ans;

int info[maxn],Prev[maxn],to[maxn],cst[maxn],cnt_e;
void Node(int u,int v,int w){
      Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=w; }
char s[maxn];
void ins(int last,int c,int n){
     
	int p = -1;
	while(s[n-len[last]-1] != s[n]){
     
		if(p != dif[last])p = dif[last],last = fa[last];
		else last = fa[anc[last]];
	}
	if(!tr[last][c]){
     
		int u = fa[last]; p = -1;
		while(s[n-len[u]-1] != s[n]){
     
			if(p != dif[u]) p = dif[u],u=fa[u];
			else u=fa[anc[u]];
		}
		fa[++tot] = tr[u][c];
		len[tot] = len[last] + 2;
		sl[tot] = sl[fa[tot]] + len[tot];
		dif[tot] = len[tot] - len[fa[tot]];
		anc[tot] = (dif[tot] == dif[fa[tot]] ? anc[fa[tot]] : fa[tot]);
		tr[last][c] = tot;
	}
}
void dfs(int u,int d){
     
	for(int i=info[u];i;i=Prev[i]){
     
		int v = to[i] , w = cst[i];
		s[d] = w;
		ins(pos[u] , w , d);
		pos[v] = tot;
		ans += sl[tot];
		dfs(v,d+1);
	}
}

int main(){
     
	
//	freopen("1.in","r",stdin);
	
	int T;
	for(scanf("%d",&T);T--;){
     
		scanf("%d",&n);
		tot = 1;
		anc[0] = fa[1] = anc[1] = fa[0] = 1,len[1] = -1;
		dif[0] = 1;
		rep(i,1,n){
     
			char ch;
			while(!isalpha(ch=getchar()));
			int u;
			scanf("%d",&u);
			Node(u,i,ch-'a');
		}
		s[0] = '#';
		dfs(0,1);
		printf("%lld\n",ans);
		ans = 0;
		rep(i,0,n) info[i] = 0;
		cnt_e = 0;
		memset(tr,0,sizeof(tr[0])*(tot+1));
		tot = 0;
	}
}

CF1266G Permutation Concatenation

1 1 1 n n n的排列按字典序连接为一个序列,序列长度为 n × n ! n\times n! n×n!,求这个序列不同的连续子序列个数。 1 ≤ n ≤ 1 e 6 1\leq n\leq 1e6 1n1e6,答案对 998244353 998244353 998244353取模。

直接搜题解
结论题,嗯。

A C   C o d e \mathcal AC \ Code AC Code

#include 
#define LL long long
#define M 998244353
using namespace std;
LL n,x, a=0;
int main() {
     
    cin >> n; x=n;
    for (LL i=0; i<n-1; ++i) {
     
        a += ((x*(n-i-1))%M)*(i+1) + M-x; a%=M;
        x = (x*(n-i))%M;
    }
    LL ans = x*(x-n+2)-2*a + M; ans%=M;
    if (ans&1) ans = (ans+M)/2;
    else ans /= 2;
    cout << ans << endl;
}

CF1043G Speckled Band

CF917E Upside Down

回忆树超级加强版

你可能感兴趣的:(字符串,数据结构)