2017多校第8场 HDU 6138 Fleet of the Eternal Throne AC自动机或者KMP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6138

题意:给n个串,每次询问x号串和y号串的最长公共子串的长度,这个子串必须是n个串中某个串的前缀

解法1:AC自动机。做法是把n个串建成AC自动机,前缀树中每个节点都当做结尾节点,val赋为trie树深度,然后把x串丢进自动机里,把匹配到的前缀节点染个色,再把y串丢进去,遇到同样颜色的前缀节点就更新一下答案。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int M = 5e5+10;
const int S = 26;
struct AcAutomata{
    int root,sz;
    int nxt[M][S],fail[M],val[M],col[N];
    int newnode(){
        val[sz] = col[sz] = 0;
        memset(nxt[sz], -1, sizeof(nxt[sz]));
        return sz++;
    }
    void init(){
        memset(val, 0, sizeof(val));
        sz = 0;
        root = newnode();
    }
    void insert(char *s){
        int u=root;
        int len=strlen(s);
        for(int i=0; i<len; i++){
            int id=s[i]-‘a‘;
            if(nxt[u][id]==-1) nxt[u][id]=newnode();
            val[nxt[u][id]]=val[u]+1;
            u=nxt[u][id];
        }
    }
    void build(){
        queue <int> q;
        fail[root] = root;
        for(int i=0; i<S; i++){
            int &v = nxt[root][i];
            if(~v){
                fail[v] = root;
                q.push(v);
            }
            else{
                v = root;
            }
        }
        while(q.size()){
            int u = q.front(); q.pop();
            for(int i = 0; i < S; i++){
                int &v = nxt[u][i];
                if(~v){
                    fail[v] = nxt[fail[u]][i];
                    q.push(v);
                }
                else{
                    v = nxt[fail[u]][i];
                }
            }
        }
    }
    void update(char *s, int x){
        int len = strlen(s);
        int u=root;
        for(int i=0; i<len; i++){
            int id=s[i]-‘a‘;
            u=nxt[u][id];
            int tmp=u;
            while(tmp){
                col[tmp]=x;
                tmp=fail[tmp];
            }
        }
    }
    int query(char *s, int x){
        int len = strlen(s);
        int u = root;
        int ans = 0;
        for(int i=0; i<len; i++){
            int id=s[i]-‘a‘;
            u=nxt[u][id];
            int tmp=u;
            while(tmp){
                if(col[tmp]==x) ans=max(ans, val[tmp]);
                tmp=fail[tmp];
            }
        }
        return ans;
    }
}ZXY;
char s[N];
int pos[N];
int main()
{
    int T,n,q;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        ZXY.init();
        int d=1;
        for(int i=1; i<=n; i++){
            pos[i]=d;
            scanf("%s", s+d);
            ZXY.insert(s+d);
            int len=strlen(s+d);
            d+=len+1;
        }
        ZXY.build();
        scanf("%d", &q);
        int id=1;
        while(q--)
        {
            int x, y;
            scanf("%d%d",&x,&y);
            ZXY.update(s+pos[x],id);
            int ans = ZXY.query(s+pos[y],id);
            ++id;
            printf("%d\n", ans);
        }
    }
    return 0;
}

解法2:KMP,直接枚举n个串做KMP。。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
vector<int> Next[maxn];
string str[maxn];
void getnext(string &s, vector<int> &nxt)
{
    int len = s.size();
    nxt.resize(len);
    nxt[0] = -1;
    int i, j = -1;
    for(i = 1; i < len; i++)
    {
        while(j >= 0 && s[j + 1] != s[i])
            j = nxt[j];
        if(s[j + 1] == s[i])
            j++;
        nxt[i] = j;
    }
}
int getMax(string &s, int strid)
{
    int len = s.size();
    int i, j = -1;
    int ret = 0;
    for(i = 0; i < len; i++)
    {
        while(j >= 0 && str[strid][j + 1] != s[i])
            j = Next[strid][j];
        if(str[strid][j + 1] == s[i])
            j++;
        ret = max(ret, j + 1);
    }
    return ret;
}
char buf[maxn];
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%s", buf);
            str[i] = buf;
            getnext(str[i], Next[i]);
        }
        int q;
        scanf("%d", &q);
        while(q--)
        {
            int x, y;
            scanf("%d %d", &x, &y);
            int ans = 0;
            for(int i = 1; i <= n; i++)
            {
                int u = getMax(str[x], i);
                int v = getMax(str[y], i);
                ans = max(ans, min(u, v));
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}
时间: 2024-10-13 09:24:14

2017多校第8场 HDU 6138 Fleet of the Eternal Throne AC自动机或者KMP的相关文章

HDU 6138 Fleet of the Eternal Throne(AC自动机)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6138 [题目大意] 给出一些串,询问第x个串和第y个串的公共子串, 同时要求该公共子串为某个串的前缀.求最长符合要求的答案 [题解] 我们对所有串构建AC自动机,将两个询问串之一在AC自动机上mark所有的匹配位置 另一个串在mark的地方寻找最长匹配即可 [代码] #include <cstdio> #include <algorithm> #include <cstrin

2017多校第6场 HDU 6096 String AC自动机

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6096 题意:给了一些模式串,然后再给出一些文本串的不想交的前后缀,问文本串在模式串的出现次数. 解法: 因为要求前缀后缀都包含的个数,所以可以把字符串a转换成a#a这样一个字符串,比如abca就转换成abca#abca 然后对于一组前缀a后缀b转换成b{a,比如ab ca,就是ca{ab, 然后对前缀后缀的串建立AC自动机,让主串去匹配,如上述例子,ca{ab满足为abca{abca的一个子串,也就

2017多校第8场 HDU 6143 Killer Names 容斥,组合计数

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6143 题意:m种颜色需要为两段长度为n的格子染色,且这两段之间不能出现相同的颜色,问总共有多少种情况. 解法:枚举要为这两段分配的颜色数目分别为 i,j ,则在第一段总共有 C(m,i) 种选取方案,在第二段总共有 C(m?i,j) 种选取方案.而在每段内部,我们设 F(n,x) 为长度为 n 的格子使用 x 种颜色(等于 x )染色的方案数.则根据容斥原理 F(n,x)=x^n?C(x,1)*(x

2017多校第4场 HDU 6078 Wavel Sequence DP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6078 题意:求两个序列的公共波形子序列的个数. 解法: 类似于最长公共上升子序列,对于每个i,只考虑存在j使得a[i]==b[j]的情况. dp[i][j][0]表示以a[i]和b[j]为公共序列结尾且为波谷的情况总和. dp[i][j][1]则表示波峰的情况总和. S[i][j][0]表示sum(dp[k][j][0] | 1<=k<=j-1). S[i][j][1]则表示sum(dp[k][j

2017多校第7场 HDU 6129 Just do it 找规律

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6129 题意:求a序列后m次xor前缀和 解法: 手动对1位置对每个位置的贡献打表发现 第一次 贡献为 1 1 1 1 1 1 1 1 1 1 1 第二次 贡献为 1 0 1 0 1 0 1 0 1 0 1 0 第四次 贡献为 1 3个0 1 3个0 1 3个0 1 3个0 第八次 贡献为 1 7个0 1 7个0 1 7个0 1 7个0 ... 这是比赛之后才知道的,看着比赛的时候通过了200+人,被

2017多校第10场 HDU 6180 Schedule 贪心,multiset

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6180 题意:给了一些任务的开始时间和终止时间,现在让我们安排k台及机器,让这些任务在k太机器上最小,并且使得机器的运行时间的和最小. 解法:按开始工作的时间从小到大排序后,用一个set容器维护一下,每次加入找set里面结束时间小于等于开始时间并且最近的点插入即可,然后如果没有小于开始时间的就重新开一台机器即可,这里可能有重复元素,需要multiset. #include <bits/stdc++.h

2017多校第9场 HDU 6170 Two strings DP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6170 题意:给了2个字符串,其中第2个字符串包含.和*两种特别字符,问第二个字符串能否和第一个匹配. 解法:dp[i][j]代表在第一个串的i位置,第2个串的j位置是否可以匹配,然后按照'*'这个特殊情况讨论转移即可. #include <bits/stdc++.h> using namespace std; const int maxn = 3005; bool dp[maxn][maxn];

2017多校第10场 HDU 6181 Two Paths 次短路

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6181 题意:给一个图,求出次短路. 解法:我之前的模板不能解决这种图,就是最短路和次短路相等的情况,证明这题数据还是水了.下来我修改了一下次短路的,就可以避免这种情况了.提供一个sample 4 4 (1,2,1)( 1, 3,1) (2 4,1) (3 ,4,1).这组的正确答案是2,算法就来看代码吧. #include <bits/stdc++.h> using namespace std;

2017多校第8场 HDU 6134 Battlestation Operational 莫比乌斯反演

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6134 题意: 解法: 那么g(n)怎么求,我们尝试打表发现g(n)是有规律的,g(n)=g(n-1)+d(n-1)+1,其中d(i)表示i的因子个数,这个我们是可以通过线性筛O(n)处理出来的,之后再O(n)维护g(i)的前缀和,就可以在单组sqrt(n)的复杂度下得到答案了. #include <bits/stdc++.h> using namespace std; typedef long l