1 条题解
-
0
说明
该代码解决的问题是:给定多个字符串,找到最长的字符串
X
,使得X
或其逆序是每个给定字符串的子串。换句话说,X
或其逆序必须出现在所有输入字符串中。算法标签
- 字符串处理
- 后缀数组
- 二分查找
解题思路
- 输入处理:读取测试用例的数量
t
,然后逐个处理每个测试用例。每个测试用例包含字符串的数量n
和具体的字符串。 - 特殊情况处理:如果只有一个字符串,直接返回该字符串的长度。
- 构建后缀数组:将所有字符串及其逆序拼接起来,中间用特殊字符分隔,构建后缀数组。
- 高度数组计算:计算后缀数组的高度数组(
Height
数组),用于后续的公共子串查找。 - 二分查找最长公共子串:使用二分查找确定最长的公共子串长度,检查是否存在一个长度为
mid
的子串或其逆序出现在所有字符串中。 - 输出结果:输出找到的最长公共子串的长度。
分析
- 后缀数组构建:将所有字符串及其逆序拼接,构建后缀数组,便于查找公共子串。
- 高度数组:高度数组记录了相邻后缀的最长公共前缀长度,是查找公共子串的关键。
- 二分查找:通过二分查找确定最长的公共子串长度,提高查找效率。
- 逆序处理:通过将字符串的逆序也加入后缀数组,确保能够同时检查子串及其逆序。
实现步骤
- 读取输入:读取测试用例的数量
t
,然后逐个处理每个测试用例。 - 处理单个字符串:如果只有一个字符串,直接返回其长度。
- 拼接字符串:将所有字符串及其逆序拼接,中间用特殊字符分隔。
- 构建后缀数组:使用构建算法生成后缀数组。
- 计算高度数组:根据后缀数组计算高度数组。
- 二分查找最长公共子串:使用二分查找确定最长的公共子串长度,检查是否满足条件。
- 输出结果:输出找到的最长公共子串的长度。
代码
#include <cstdio> #include <string> #include <algorithm> #include <iostream> #include <set> using namespace std; const int N = 3e4; const int NN = 100; const int MAX_CHAR = 1000;//每个数字的最大值。 int s[N + 10];//如果是数字,就写成int s[N+10]就好,从0开始存 int Sa[N + 10], T1[N + 10], T2[N + 10], C[N + 10]; int Height[N + 10], Rank[N + 10], idx[N + 10]; int n, len; void build_Sa(int n, int m) { int i, *x = T1, *y = T2; for (i = 0; i<m; i++) C[i] = 0; for (i = 0; i<n; i++) C[x[i] = s[i]]++; for (i = 1; i<m; i++) C[i] += C[i - 1]; for (i = n - 1; i >= 0; i--) Sa[--C[x[i]]] = i; for (int k = 1; k <= n; k <<= 1) { int p = 0; for (i = n - k; i<n; i++) y[p++] = i; for (i = 0; i<n; i++) if (Sa[i] >= k) y[p++] = Sa[i] - k; for (i = 0; i<m; i++) C[i] = 0; for (i = 0; i<n; i++) C[x[y[i]]]++; for (i = 1; i<m; i++) C[i] += C[i - 1]; for (i = n - 1; i >= 0; i--) Sa[--C[x[y[i]]]] = y[i]; swap(x, y); p = 1; x[Sa[0]] = 0; for (i = 1; i<n; i++) x[Sa[i]] = y[Sa[i - 1]] == y[Sa[i]] && y[Sa[i - 1] + k] == y[Sa[i] + k] ? p - 1 : p++; if (p >= n) break; m = p; } } void getHeight(int n) { int i, j, k = 0; for (i = 1; i <= n; i++) Rank[Sa[i]] = i; for (i = 0; i<n; i++) { if (k) k--; j = Sa[Rank[i] - 1]; while (s[i + k] == s[j + k]) k++; Height[Rank[i]] = k; } } bool ok(int l) { set <int> mset; mset.clear(); for (int i = 2; i <= len; i++) if (Height[i] >= l) { mset.insert(idx[Sa[i - 1]]); mset.insert(idx[Sa[i]]); } else { if ((int)mset.size() == n) return true; mset.clear(); } if ((int)mset.size() == n) return true; return false; } int main() { //freopen("F:\\rush.txt", "r", stdin); int T; cin >> T; while (T--) { int special = -1; len = 0; int spe = 'z' + 1; cin >> n; for (int i = 1; i <= n; i++) { string S; cin >> S; int ls = S.size(); if (n == 1) special = ls; for (int j = 0; j < ls; j++) { idx[len] = i; s[len++] = S[j]; } s[len++] = spe++; reverse(S.begin(), S.end()); for (int j = 0; j < ls; j++) { idx[len] = i; s[len++] = S[j]; } s[len++] = spe++; } if (special != -1) { cout << special << endl; continue; } s[len] = 0; build_Sa(len + 1, MAX_CHAR); getHeight(len); //开始写二分部分 int l = 1, r = 100, temp = 0;//如果l的初始值为0的话,ok函数最后那行集合大小为n的判断就不能 //省掉,因为所有函数的Height值都为0的>_<,集合大小为n的判断就漏掉了 while (l <= r) { int mid = (l + r) >> 1; if (ok(mid)) { temp = mid; l = mid + 1; } else r = mid - 1; } cout << temp << endl; } return 0; }
- 1
信息
- ID
- 227
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者