Description

标点符号的出现晚于文字的出现, 所以以前的语言都是没有标点的. 现在你要处理的就是一段没有标点的文章. 一段文章 \(T\) 是由若干小写字母构成. 一个单词 \(W\) 也是由若干小写字母构成. 一个字典 \(D\) 是若干个单词的集合. 我们称一段文章 \(T\) 在某个字典 \(D\) 下是可以被理解的, 是指如果文章 \(T\) 可以被分成若干部分, 且每一个部分都是字典 \(D\) 中的单词. 例如字典 \(D\) 中包括单词 \(\{‘is’, ‘name’, ‘what’, ‘your’\}\), 则文章 \(`whatisyourname'\) 是在字典 \(D\) 下可以被理解的因为它可以分成 \(4\) 个单词:\(‘what’, ‘is’, ‘your’, ‘name’\), 且每个单词都属于字典 \(D\), 而文章 \(‘whatisyouname’\) 在字典 \(D\) 下不能被理解, 但可以在字典 \(D’=D+\{‘you’\}\) 下被理解. 这段文章的一个前缀 \(‘whatis’\), 也可以在字典 \(D\) 下被理解 而且是在字典 \(D\) 下能够被理解的最长的前缀. 给定一个字典 \(D\), 你的程序需要判断若干段文章在字典 \(D\) 下是否能够被理解. 并给出其在字典 \(D\) 下能够被理解的最长前缀的位置.

Input

输入文件第一行是两个正整数 \(n\)\(m\), 表示字典 \(D\) 中有 \(n\) 个单词, 且有 \(m\) 段文章需要被处理. 之后的 \(n\) 行每行描述一个单词, 再之后的 \(m\) 行每行描述一段文章. 其中 \(1 \leq n, m \leq 20\), 每个单词长度不超过 \(10\), 每段文章长度不超过 \(10^6\) 个字节.

Output

对于输入的每一段文章, 你需要输出这段文章在字典 \(D\) 可以被理解的最长前缀的位置.

Sample Input

4 3
is
name
what
your
whatisyourname
whatisyouname
whaisyourname

Sample Output

14
6
0

Explanation

考虑这些单词都比较短, 且 Trie 树的节点数不超过 \(200\) 个, 我们可以直接在树上以文章 \(S\) 为对象, 字典 \(D\) 为转移条件, 跑一遍 DP.

(看到一坨字符串就想到 Trie 或 AC 自动机对不对)

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>

using namespace std;
typedef long long lli;
const int maxn = 310, maxm = 2000100;

// class Trie
// {
// public:
    int ncnt, root;
    int ch[maxn][26];
    int dp[maxm];
    bool flag[maxn];
    int make_node(void)
    {
        int p = ncnt++;
        for (int i = 0; i < 26; i++)
            ch[p][i] = 0;
        return p;
    }
    void insert(char str[])
    {
        int p = root;
        for (int i = 0; str[i] != '\0'; i++) {
            int v = str[i] - 'a';
            if (!ch[p][v])
                ch[p][v] = make_node();
            p = ch[p][v];
        }
        flag[p] = true;
        return ;
    }
    int match(char str[])
    {
        int len = strlen(str + 1);
        memset(dp, 0, sizeof(dp));
        dp[0] = true;
        int tmp = 0, res = 0;
        for (int i = 0; i <= len; i++) {
            if (tmp > 10) break;
            if (!dp[i]) {
                tmp += 1;
                continue;
            }
            tmp = 0; res = i;
            // Extending string on length
            for (int j = i + 1, p = ch[root][str[j] - 'a']; p; p = ch[p][str[++j] - 'a']) {
                if (flag[p]) dp[j] = true;
                if (j == len) break;
            }
        }
        return res;
    }
    void init(void)
    {
        ncnt = 0;
        root = make_node();
        return ;
    }
// } tr;

int n, m;
char str[maxm];

int main(int argc, char** argv)
{
    scanf("%d%d", &n, &m);
    /*tr.*/ init();
    for (int i = 1; i <= n; i++) {
        scanf("%s", str);
        /*tr.*/ insert(str);
    }
    // Matching strings
    for (int i = 1; i <= m; i++) {
        scanf("%s", str + 1);
        int res = /*tr.*/ match(str);
        printf("%d\n", res);
    }
    return 0;
}