洛谷P2322 思路分享(AC 自动机,分层图最短路)

https://www.luogu.com.cn/problem/P2322

题意概述

给定 \(n\) 个字符串,找到字典序最小的最短字符串 \(T\),使得所有给定字符串都是 \(T\) 的子串.

\(1\le n \le 12\),每个字符串长度不超过 \(50\).

思路

多模式串匹配,考虑 AC 自动机.

建出 AC 自动机后,令 \(ref_u\) 为以节点 \(u\) 结尾能匹配哪些给定的字符串,用位掩码表示,注意 \(ref_u\) 是可以继承 \(ref_{fail_u}\) 的.

处理完这些,跑分层图最短路即可,记录一下前驱回溯构造方案.

时间复杂度 \(\mathcal{O}(26L\cdot 2^n)\)\(L\)\(n\) 个字符串的长度之和.

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int INF = 1e9;

void solve(){
	int n;
	cin >> n;

	vector<string> s(n+1);
	for (int i=1;i<=n;i++){
		cin >> s[i];
	}	

	vector<int> ref{0};
	vector<vector<int>> next{vector<int>(26)};

	for (int i=1;i<=n;i++){
		int u = 0;
		for (auto& ch:s[i]){
			int c = ch-'A';
			if (next[u][c]==0){
				next.push_back(vector<int>(26));
				ref.push_back(0);
				next[u][c] = next.size()-1;
			}

			u = next[u][c];
		}

		ref[u] |= 1<<i-1;
	}

	int m = next.size();
	vector<int> fail(m);

	queue<int> q;
	for (int c=0;c<26;c++){
		if (next[0][c]){
			q.push(next[0][c]);
		}
	}

	while (!q.empty()){
		int u = q.front();
		q.pop();

		for (int c=0;c<26;c++){
			if (next[u][c]){
				fail[next[u][c]] = next[fail[u]][c];
                ref[next[u][c]] |= ref[fail[next[u][c]]];
				q.push(next[u][c]);	
			}
			else{
				next[u][c] = next[fail[u]][c];
			}
		}
	}

	queue<pair<int,int>> qe;
	qe.emplace(0,0);

	vector<vector<int>> dis(m,vector<int>(1<<n,INF));
	vector<vector<array<int,3>>> pre(m,vector<array<int,3>>(1<<n,{-1,-1,-1}));
	dis[0][0] = 0;
	int last = -1;

	while (!qe.empty()){
		auto [mask,u] = qe.front();
		qe.pop();

		for (int c=0;c<26;c++){
			int v = next[u][c];
			int nmask = mask | ref[v];
			if (dis[u][mask]+1<dis[v][nmask]){
				dis[v][nmask] = dis[u][mask]+1;
				pre[v][nmask] = {c,mask,u};
				qe.emplace(nmask,v);

				if (nmask==(1<<n)-1){
					last = v;
					break;
				}
			}
		}
		if (last!=-1) break;
	}

	// cout << last << '\n';

	string res;
	int cur = last;
	int mask = (1<<n)-1;
	while (cur!=0){
		res += (char)(pre[cur][mask][0]+'A');
		int nmask = pre[cur][mask][1];
		int v = pre[cur][mask][2];

		mask = nmask;
		cur = v;
	}

	reverse(res.begin(),res.end());
	cout << res << '\n';
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	// cin >> t;
	while (t--) solve();

	return 0;
} 
posted @ 2026-06-12 10:59  kzssCCC  阅读(14)  评论(0)    收藏  举报