• poj 3415 后缀数组+单调栈


    依旧是按照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 }
  • 相关阅读:
    TCP IP基础知识的复习
    Design Pattern: Singleton 模式
    解决Win7下安装VS2010不显示序列号框的两种方法
    字典树(Trie tree)
    在VS如何查看汇编代码
    使用模板实现编译期间多态
    一段c++代码小例子
    C++ 虚函数表解析
    C++问题:if( input.rdstate() & std::ios::failbit )
    Design Pattern: Adapter 模式 Class Adapter
  • 原文地址:https://www.cnblogs.com/nuchenghao/p/11297957.html
Copyright © 2020-2023  润新知