【题解】Codeforces 727E. Games on a CD AC自动机

给定 n , k ( 1 e 5 , n k 1 e 6 ) n,k(1e5,n*k\le 1e6) ,给定一个长为 n k n*k 的环形字符串,已知它由 n n 个长为 k k 的互不相同字符串相接而成。

再给定 g ( g n ) g(g\ge n) 个长为 k k 的字符串,两两不同,总长不超过 2 e 6 2e6 .

问环形字符串是由哪些字符串首尾相接(每个字符串只能出现一次)组成的,输出时可以选定任意的起点,无解输出 1 -1


将环形字符串展开到 n k + k 1 nk+k-1 长度,因为所有字符串长度相等,所以每个位置只会匹配一个模式串。用AC自动机找到每个位置所匹配的编号。

把位置按模 n n 分组,对于每个模数的 n n 个位置,看看是否有重复的模式串编号,如果没有就是一组解。


这是2300的最后一道题目,感觉2300以下的题有不少kmp的好题,但是一旦涉及自动机就会变得模板了起来,qwq.

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 2500016, MOD = 1000000007;


//Trie树(图)/是否为终结点/失配链接/后缀链接,都是节点的属性
int sz = 0;
int ch[M][26], ed[M], fail[M], last[M];

// 向Trie树中尝试插入一个模式串, 返回插入后的节点编号
void insert(const char *s, int id)
{
    int u = 0;
    for(int i = 0; s[i]; i++)
    {
        int &v = ch[u][s[i] - 'a'];
        if(!v) v=++sz;
        u = v;
    }
    ed[u] = id;
}

void build(const char *s, vector<int> *match, int k)
{
	queue<int> q;
    for(int v:ch[0]) if(v)
        fail[v] = 0, q.push(v);
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int i = 0; i < 26; i++)
        {
            int &v = ch[u][i];
            if(v) 
            {
                q.push(v);
                fail[v] = ch[fail[u]][i];
                last[v] = ed[fail[v]] ? fail[v] : last[fail[v]]; //后缀链接
            }
            else v = ch[fail[u]][i]; //建立trie图
        }
    }

    int now = 0;
    for(int i = 0; s[i]; i++)
    {
        now = ch[now][s[i] - 'a'];
        int id = ed[now] ? now : last[now]; 
        if(ed[id]) match[i%k].push_back(ed[id]); //最多只会匹配一个
    }
}

char str[M], pat[M];
vector<int> match[100016]; //每个模数依次匹配的编号
int tag[M];
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	int n = read(), k = read(), m = n*k+k-1;
	scanf("%s", str);
	for(int i=n*k; i<m; ++i)
		str[i] = str[i-n*k];
	int g = read();
	for(int i=1; i<=g; ++i)
	{
		scanf("%s", pat);
		insert(pat, i);
	}
	build(str, match, k);
	memset(tag, -1, sizeof(tag));
	for(int i=0; i<k; ++i) if((int)match[i].size()==n)
	{
		int suc = 1;
		for(auto x:match[i])
		{
			if(tag[x]==i)
			{
				suc = 0;
				break;
			}
			tag[x] = i;
		}
		if(suc)
		{
			printf("YES\n");
			for(auto x:match[i])
				printf("%d ",x );
			return 0;
		}
	}
	printf("NO\n");


    return 0;
}

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
发布了375 篇原创文章 · 获赞 305 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/103192061
今日推荐