【題解】Codeforces 727E. Games on a CD AC自動機

給定n,k(1e5,nk1e6)n,k(1e5,n*k\le 1e6),給定一個長爲nkn*k的環形字符串,已知它由nn個長爲kk的互不相同字符串相接而成。

再給定g(gn)g(g\ge n)個長爲kk的字符串,兩兩不同,總長不超過2e62e6.

問環形字符串是由哪些字符串首尾相接(每個字符串只能出現一次)組成的,輸出時可以選定任意的起點,無解輸出1-1


將環形字符串展開到nk+k1nk+k-1長度,因爲所有字符串長度相等,所以每個位置只會匹配一個模式串。用AC自動機找到每個位置所匹配的編號。

把位置按模nn分組,對於每個模數的nn個位置,看看是否有重複的模式串編號,如果沒有就是一組解。


這是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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章