问题描述:先给你s个禁止串,求不包含禁止串的最长串,如果存在,打印字典序最大。
数据范围:s <= 1000, 禁止串长度不超过50。
分析:不匹配问题实际上等同于匹配问题。假设我们已经有满足条件的串T, 如果加上某个字符c后得到的新串T + c 仍然满足条件,显然
我们便找到了一个更长的串,否则|T|就是所求。我们枚举字符c时,新串是否满足条件取决于原串T的后缀关于所有禁止串在其构成的trie
上的匹配情况,这也就是我们需要并且仅需要维护的信息。具体的说,我们记录串T在trie中匹配最深的节点编号,假设为l(T),当在T尾部
追加字符c时:
l(T) = nex[l(T)][c]
T = T + c
如果更新后得到的l(T)不在任一个禁止节点上,新串便是符合要求的。
如此我们看,若存在一个长度有限的最长串T,那么在T后追加任一个字符c后都会使得l(T + c)落在某个禁止节点上。考虑nex数组的计算:
nex[i][j]表示节点i对应的前缀追加字符j后在trie中匹配最深的节点编号。显然:
nex[i][j] = ch[i][j] ? ch[i][j] : ch[fail[i]][j]]
这里fail[i]表示节点i对应的前缀的后缀(不包括其本身)在trie中匹配最深的节点编号,即AC自动机中的失配函数。
将nex数组合并到ch数组中:
ch[i][j] = nex[i][j]
于是由trie中所有节点关于ch函数构成的一张有向图,并且将某些节点标记为禁止节点当且仅的其对应的前缀为某个禁止串。
因此考虑删去禁止节点后的新图G = (V, A), 合法串与图上的路径有一一对应关系,最长串存在当且仅当 |V| > 1(不能仅包含trie中0节点)
且图中无环。
判断一张有向图是否是DAG可以使用栈的性质,栈中存储dfs搜索经过的链节点,若新点不在链中即可加入,否则证明有环。
1 #include <algorithm> 2 #include <cstdio> 3 #include <cstring> 4 #include <string> 5 #include <queue> 6 #include <map> 7 #include <set> 8 #include <ctime> 9 #include <cmath> 10 #include <iostream> 11 #include <assert.h> 12 #define pi acos(-1.) 13 using namespace std; 14 typedef long long ll; 15 const int int_inf = 0x3f3f3f3f; 16 const ll ll_inf = 1ll << 62; 17 const int INT_INF = (int)((1ll << 31) - 1); 18 const int mod = 1e9 + 7; 19 const double double_inf = 1e30; 20 typedef unsigned long long ul; 21 #pragma comment(linker, "/STACK:102400000,102400000") 22 #define max(a, b) ((a) > (b) ? (a) : (b)) 23 #define min(a, b) ((a) < (b) ? (a) : (b)) 24 #define mp make_pair 25 #define st first 26 #define nd second 27 #define keyn (root->ch[1]->ch[0]) 28 #define lson (u << 1) 29 #define rson (u << 1 | 1) 30 #define pii pair<int, int> 31 #define pll pair<ll, ll> 32 #define pb push_back 33 #define type(x) __typeof(x.begin()) 34 #define foreach(i, j) for(type(j)i = j.begin(); i != j.end(); i++) 35 #define FOR(i, s, t) for(int i = (s); i <= (t); i++) 36 #define ROF(i, t, s) for(int i = (t); i >= (s); i--) 37 #define dbg(x) cout << x << endl 38 #define dbg2(x, y) cout << x << " " << y << endl 39 #define clr(x, i) memset(x, (i), sizeof(x)) 40 #define maximize(x, y) x = max((x), (y)) 41 #define minimize(x, y) x = min((x), (y)) 42 #define low_bit(x) ((x) & (-x)) 43 44 inline int readint(){ 45 int x; 46 scanf("%d", &x); 47 return x; 48 } 49 50 inline int readstr(char *s){ 51 scanf("%s", s); 52 return strlen(s); 53 } 54 55 class cmpt{ 56 public: 57 bool operator () (const int &x, const int &y) const{ 58 return x > y; 59 } 60 }; 61 62 int Rand(int x, int o){ 63 //if o set, return [1, x], else return [0, x - 1] 64 if(!x) return 0; 65 int tem = (int)((double)rand() / RAND_MAX * x) % x; 66 return o ? tem + 1 : tem; 67 } 68 69 void data_gen(){ 70 srand(time(0)); 71 freopen("in.txt", "w", stdout); 72 int times = 10; 73 printf("%d ", times); 74 while(times--){ 75 int n = Rand(500, 1), m = Rand(500, 1); 76 printf("%d %d ", n, m); 77 FOR(i, 1, n){ 78 FOR(j, 1, m) printf("%c", Rand(2, 0) + 'a'); 79 putchar(' '); 80 } 81 n = Rand(min(10, n), 1), m = Rand(min(10, m), 1); 82 printf("%d %d ", n, m); 83 FOR(i, 1, n){ 84 FOR(j, 1, m) printf("%c", Rand(2, 0) + 'a'); 85 putchar(' '); 86 } 87 } 88 } 89 90 struct cmpx{ 91 bool operator () (int x, int y) { return x > y; } 92 }; 93 int debug = 1; 94 int dx[] = {-1, 1, 0, 0}; 95 int dy[] = {0, 0, -1, 1}; 96 //------------------------------------------------------------------------- 97 const int maxn = 1e3 + 10; 98 const int maxm = 55; 99 int sigma_size, tot; 100 struct Trie{ 101 int ch[maxn * maxm][26]; 102 int sz; 103 int info[maxn * maxm]; 104 int fail[maxn * maxm]; 105 int ans[maxn * maxm]; 106 int ok[maxn * maxm]; 107 void init() { sz = tot = 0; clr(ch[0], 0); clr(info, 0); } 108 int idx(char c) { return c - 'A'; } 109 void insert(char *s){ 110 int u = 0; 111 while(*s){ 112 int v = ch[u][idx(*s)]; 113 if(!v) { ch[u][idx(*s)] = v = ++sz; clr(ch[sz], 0); } 114 u = v; 115 ++s; 116 } 117 if(!info[u]) info[u] = ++tot; 118 } 119 void getFail(){ 120 queue<int> q; 121 while(!q.empty()) q.pop(); 122 FOR(i, 0, sigma_size - 1) if(ch[0][i]) q.push(ch[0][i]); 123 FOR(i, 0, sigma_size - 1) fail[ch[0][i]] = 0; 124 while(!q.empty()){ 125 int u = q.front(); q.pop(); 126 FOR(i, 0, sigma_size - 1){ 127 int v = ch[u][i]; 128 if(!v) { ch[u][i] = ch[fail[u]][i]; continue; } 129 fail[v] = ch[fail[u]][i]; 130 q.push(v); 131 } 132 } 133 } 134 135 bool vis[maxn * maxm]; 136 bool dfs(int u){ 137 if(vis[u]) return true; 138 if(ok[u] != -1) return ok[u]; 139 if(info[u]) return ok[u] = 0; 140 vis[u] = 1; 141 FOR(i, 0, sigma_size - 1){ 142 int v = ch[u][i]; 143 if(dfs(v)) { vis[u] = 0; return ok[u] = 1; } 144 } 145 vis[u] = 0; 146 return ok[u] = 0; 147 } 148 149 bool isAcy(){ 150 clr(ok, -1), clr(vis, 0); 151 return !dfs(0); 152 } 153 154 int __dfs(int u){ 155 if(ans[u] != -1) return ans[u]; 156 if(info[u]) return ans[u] = 0; 157 int maxi = 0; 158 FOR(i, 0, sigma_size - 1){ 159 int v = ch[u][i]; 160 int tem = __dfs(v); 161 maximize(maxi, tem); 162 } 163 return ans[u] = maxi + 1; 164 } 165 int getAns(){ 166 clr(ans, -1); 167 return __dfs(0); 168 } 169 void printAns(int u, int d){ 170 if(info[u]) return; 171 ROF(i, sigma_size - 1, 0){ 172 int v = ch[u][i]; 173 if(ans[v] == d - 1){ 174 if(ans[v] > 0) putchar(i + 'A'); 175 printAns(v, d - 1); 176 return; 177 } 178 } 179 } 180 }trie; 181 int n; 182 char s[maxm]; 183 //------------------------------------------------------------------------- 184 int main(){ 185 //data_gen(); return 0; 186 //C(); return 0; 187 debug = 0; 188 /////////////////////////////////////////////////////////////////////////////////////////////////////////////// 189 if(debug) freopen("in.txt", "r", stdin); 190 //freopen("out.txt", "w", stdout); 191 int T = readint(); 192 while(T--){ 193 sigma_size = readint(), n = readint(); 194 trie.init(); 195 FOR(i, 1, n){ 196 readstr(s); 197 trie.insert(s); 198 } 199 trie.getFail(); 200 int ans; 201 if(!trie.isAcy()) ans = 0; 202 else ans = trie.getAns(); 203 if(ans > 1) trie.printAns(0, ans), putchar(' '); 204 else puts("No"); 205 } 206 ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 207 return 0; 208 }