【NOIP2017提高A组冲刺11.8】好文章 ——联赛字符串算法复习

题目大意:

给出长度为n的串,问这个串长度为m的子串有多少个不同的。

题解:

这其实是一道SA的裸题,求出height以后,先把长度小于m的后缀删去,然后就这样取,如果一个后缀和前面最后的长度大于等于m的后缀之间的height的最小值小于m,则这个可取。
SA的板子背的还是不够熟啊,又去看了一波biao。

然而这是一道联赛题。
所以hash就好了。
当然单hash被卡了,那就双hash,两个质数,两个模数,用set的pair直接搞一下,挺好打的。
题解用神马斯特林公式算了期望正确率,还挺高的。

Code(SA):

#include<cstdio> 
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

const int N = 2e5 + 5;

int n, m, mm; char a[N];
int tp[N], tax[N], SA[N], rank[N], he[N];

void Rsort() {
    fo(i, 1, m) tax[i] = 0;
    fo(i, 1, n) tax[rank[tp[i]]] ++;
    fo(i, 1, m) tax[i] += tax[i - 1];
    fd(i, n, 1) SA[tax[rank[tp[i]]] --] = tp[i];
}
bool cmp(int *f, int x, int y, int c) {return f[x] == f[y] && f[x + c] == f[y + c];}
void Suffix() {
    fo(i, 1, n) rank[i] = a[i], tp[i] = i;
    m = 128; Rsort();
    for(int w = 1, p = 1; p < n; w <<= 1, m = p) {
        p = 0; fo(i, n - w + 1, n) tp[++ p] = i;
        fo(i, 1, n) if(SA[i] > w) tp[++ p] = SA[i] - w;
        Rsort(), swap(rank, tp), rank[SA[1]] = p = 1;
        fo(i, 2, n) rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++ p;
    }
    int j = 0, k = 0;
    for(int i = 1; i <= n; he[rank[i ++]] = k)
        for(k = k ? k - 1 : k, j = SA[rank[i] - 1]; a[i + k] == a[j + k] && max(i, j) + k <= n; k ++);
}

int main() {
    freopen("article.in", "r", stdin);
    freopen("article.out", "w", stdout);
    scanf("%d %d", &n, &mm);
    scanf("%s", a + 1);
    Suffix();
    fo(i, 2, n - 1) if(SA[i] + mm - 1 > n)
        he[i + 1] = min(he[i + 1], he[i]);
    int fi = 0; int ans = 0;
    fo(i, 1, n) if(SA[i] + mm - 1 <= n) {
        if(!fi) {
            fi = 1;
            ans ++;
            continue;
        }
        if(he[i] < mm) ans ++;
    }
    printf("%d", ans);
}

Code(hash):

#include<set>
#include<cstdio> 
#include<cstring>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(ll i = x; i <= y; i ++)
#define fd(i, x, y) for(ll i = x; i >= y; i --)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

const ll N = 2e5 + 5;

ll n, m; char a[N];

const ll p1 = 15312137, p2 = 3113791, mo = 1e9 + 7, mo2 = 1e9 + 9;

ll c[N], d[N];

set<pair<int, int> > s;

int main() {
    freopen("article.in", "r", stdin);
    freopen("article.out", "w", stdout);
    scanf("%d %d", &n, &m);
    c[0] = 1; fo(i, 1, n) c[i] = c[i - 1] * p1 % mo;
    d[0] = 1; fo(i, 1, n) d[i] = d[i - 1] * p2 % mo2;
    scanf("%s", a + 1);
    ll z = 0, e = 0;
    fo(i, 1, m)
        z = (z + c[m - i] * a[i] % mo) % mo,
        e = (e + d[m - i] * a[i] % mo2) % mo2;
    s.insert(make_pair(z, e));
    fo(i, m + 1, n) {
        z = (z - c[m - 1] * a[i - m] % mo + mo) % mo;
        z = z * p1 % mo;
        z = (z + a[i]) % mo;

        e = (e - d[m - 1] * a[i - m] % mo2 + mo2) % mo2;
        e =  e * p2 % mo2;
        e = (e + a[i]) % mo2;

        s.insert(make_pair(z, e));
    }
    printf("%d", s.size());
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值