依旧是按照height数组分组的思想。
此处便于计算,将height重新定义为两个相邻名次的后缀的最长公共前缀共能产生多少个长度为k的公共子串。计算A的所有后缀和B的所有后缀之间的最长公共前缀的长度,把其中最长公共前缀长度不小于k的部分全部加起来。
具体方法:将字符串A和B连起来,中间用一个没有出现过的字符隔开,按height值分组后,接下来便是快速统计每组中后缀之间的最长公共前缀之和。扫描一遍,每遇到一个B的后缀就统计与前面A的后缀能产生多少个长度不小于k的公共子串,用一个单调栈来维护。
1 #include<iostream> 2 #include<algorithm> 3 #include<string> 4 using namespace std; 5 int k; 6 #define maxn 200010 7 int n; 8 struct node 9 { 10 int now, next; 11 } d[maxn]; 12 string S, s,str; 13 int val[maxn][2], c[maxn], Rank[maxn], SA[maxn], pos[maxn], x[maxn]; 14 int h[maxn]; 15 int height[maxn]; 16 void add_value(int u, int v, int i) 17 { 18 d[i].next = c[u]; 19 c[u] = i; 20 d[i].now = v; 21 } 22 void radix_sort(int l, int r) 23 { 24 for (int k = 1; k >= 0; k--) 25 { 26 memset(c, 0, sizeof(c)); 27 for (int i = r; i >= l; i--) 28 add_value(val[pos[i]][k], pos[i], i); 29 int t = 0; 30 for (int i = 0; i <= maxn; i++) 31 for (int j = c[i]; j; j = d[j].next)pos[++t] = d[j].now; 32 } 33 int t = 0; 34 for (int i = 1; i <= n; i++) 35 { 36 if (val[pos[i]][0] != val[pos[i - 1]][0] || val[pos[i]][1] != val[pos[i - 1]][1])t++; 37 Rank[pos[i]] = t; 38 } 39 } 40 void get_suffix_array() 41 { 42 int t = 1; 43 while (t / 2 <= n) 44 { 45 for (int i = 1; i <= n; i++) 46 { 47 val[i][0] = Rank[i]; 48 val[i][1] = (((i + t / 2 <= n) ? Rank[i + t / 2] : 0)); 49 pos[i] = i; 50 } 51 radix_sort(1, n); 52 t *= 2; 53 } 54 for (int i = 1; i <= n; i++) 55 SA[Rank[i]] = i; 56 } 57 void get_common_prefix() 58 { 59 memset(h, 0, sizeof(h)); 60 for (int i = 1; i <= n; i++) 61 { 62 if (Rank[i] == 1)h[i] = 0; 63 else 64 { 65 int now = 0; 66 if (i > 1 && h[i - 1] > 1)now = h[i - 1] - 1; 67 while (now + i <= n&&now + SA[Rank[i] - 1] <= n&&x[now + i] == x[now + SA[Rank[i] - 1]]) 68 now++; 69 h[i] = now; 70 } 71 } 72 for (int i = 1; i <= n; i++) 73 height[Rank[i]] = h[i]; 74 } 75 int ans; 76 int sum1[maxn],sum2[maxn]; 77 int sta[maxn]; 78 int num1[maxn], num2[maxn];//num数组累计个数 79 void get_ans() 80 { 81 //对height重定义为相邻两个名次的后缀的最长公共前缀共产生多少个长度为k的公共子串 82 for (int i = 2; i <= n; i++) 83 height[i] -= k - 1;//大于0,则可以产生height[i]-k+1个长度为k的公共子串 84 //使得名次为i和i-1的后缀产生的height[i]个长度至少为k的公共子串 85 long long sum1 = 0, sum2 = 0, ans = 0; 86 87 //ans长度至少为k的重复子串数 88 //sum1[]为前一名次的公共前缀在串1的数量 89 //sum2[]为前一名次的公共前缀在串2的数量 90 91 92 int top = 0;//初始时 栈为空 93 for (int i = 2; i <= n; i++) 94 if (height[i] <= 0)//未产生长度 95 { 96 top = sum1 = sum2 = 0; 97 } 98 else //排名为i与排名i-1的两个有后缀有height[i]个长度为k的公共子串,则子串数入栈 99 { 100 sta[++top] = height[i];//sta记录的是能产生的次数 101 102 if (SA[i - 1] <= (int)S.size())//若名次为i-1的公共前缀在串1,则标志入栈,子串计入sum1 103 { 104 num1[top] = 1; num2[top] = 0; 105 sum1 += (long long)sta[top]; 106 } 107 else 108 { 109 num1[top] = 0; num2[top] = 1; 110 sum2 += (long long)sta[top]; 111 } 112 //后缀间的最长公共前缀为height值的最小值,满足单调性,可用单调栈来维护 113 /* 114 ----- height1 115 ------------ height2 116 -------- height3 117 当对于同一组而言(假设组内满足的height数量很多),对于当前的sum1是height1和之前产生的贡献, 118 先减去height1,再加上height2的贡献 119 */ 120 while (top > 0 && sta[top] <= sta[top - 1])//若栈顶元素值不大于次栈顶元素值,这调整,维护单调性 121 { 122 sum1 = sum1 - (long long)sta[top - 1] * num1[top - 1] + (long long)sta[top] * num1[top - 1]; 123 sum2 = sum2 - (long long)sta[top - 1] * num2[top - 1] + (long long)sta[top] * num2[top - 1]; 124 num1[top - 1] += num1[top]; 125 num2[top - 1] += num2[top]; 126 sta[top - 1] = sta[top];//栈顶值下移 127 top--;//出栈 128 } 129 //如果满足单调性,直接加即可 130 if (SA[i] <= (int)S.size())//若名次为i的公共前缀在串1,则累计前面串2的后缀产生的 131 //公共子串数,否则累计前面串1的后缀产生的 132 ans += sum2; 133 else ans += sum1; 134 } 135 cout << ans << endl; 136 } 137 int main() 138 { 139 while (cin >> k, k > 0) 140 { 141 cin >> S >> s; 142 n = (int)S.size() + s.size() + 1; 143 //这里有两个串合并,最大值至少要为单串的两倍 144 str = S + '$' + s; 145 for (int i = 1; i <= n; i++) 146 x[i] = Rank[i] = (int)str[i - 1]; 147 get_suffix_array(); 148 get_common_prefix(); 149 get_ans(); 150 } 151 return 0; 152 }