学习系列 - 字符串匹配

字符串算法有哪些呢???Tire,BM,KMP,AC自动机,后缀数组,后缀自动机,RK,Shift-And/Or,Manacher.....

™这么这么多啊!!!

也只能慢慢学了。。。【后期:好多算法都是没用的鬼。。

接下来的题是按我做题顺序来排的,难度的话我就不理了(`・ω・´)

 


 

先是BZOJ。。。

BZOJ 2434: [NOI2011]阿狸的打字机

第一道字符串就做这道简直大丈夫!

先建棵ACTree和FailTree,保存每个字符串的末节点位置。

然后从树根一步一步走,根至当前节点的字符串就是当前打字机内的字符串,一边走一边离线查询。

对于每个查询(x,y)在查到s[y]的末尾节点的时候开始计算,用DFS序思想和树状数组求s[x]的末尾节点的Fail子树中包含了几个s[y]的节点,这就是答案了。

当时AC自动机不会,FailTree不会,后缀数组不会,DFS序没想到。。。QAQ

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 123456
#define MAX 1037471823

using namespace std;

struct node 
{ 
    int x, y, n; 
    bool operator < (const node &k) const { return x < k.x; };
} e[MS]; int ec, fir[MS];
int c[MS], r[MS][26], f[MS], h[MS], tc, ss[MS], ds[MS], dt[MS], k[MS];
queue<int> p;
int n, m, x, y, now, nq;
char s[MS];

void DFS(int x)
{
    int o = fir[x]; ds[x] = ++now;
    while (o) { DFS(e[o].y); o = e[o].n; }
    dt[x] = now;
}

inline void Add(int x, int z)
    { int o = x; while (o<=tc+1) k[o]+=z, o = o+(o&(-o));  }

inline int Qsum(int x)
{
    int o = x, ans=0; while (o) ans+=k[o], o = o-(o&(-o));  
    return ans;
}

inline void Query(int x)
{
    while (e[nq].x == x)
        { int y=e[nq].y; e[nq].y = Qsum(dt[ss[y]]) - Qsum(ds[ss[y]]-1); nq++; }
}
    
int main()
{
    scanf("%s", s); int l=strlen(s); f[0] = -1;
    rep(i, 0, l-1) 
        if (s[i]!='P' && s[i]!='B') 
            { if (!r[now][s[i]-'a']) tc++, r[now][s[i]-'a']=tc, h[tc]=now, c[tc]=s[i]-'a'; now=r[now][s[i]-'a']; }
        else if (s[i]=='P') ss[++n] = now; 
        else now = h[now];
    rep(i, 0, tc) f[i] = -1; p.push(0);
    while (!p.empty())
    {
        x = p.front(); p.pop();
        rep(i, 0, 25) if (r[x][i])
        {
            int fo = f[x]; 
            while (fo >= 0)
                { if (r[fo][i]) break; else fo = f[fo]; }
            if (fo < 0) f[r[x][i]] = 0; else f[r[x][i]] = r[fo][i];
            p.push(r[x][i]);
        }
    }
    down(i, tc, 1) e[i].y = i, e[i].n = fir[f[i]], fir[f[i]] = i; now = 0; DFS(0);
    rep(i, 1, tc) fir[i] = 0;
    scanf("%d", &m);
    rep(i, 1, m)
        { scanf("%d%d", &x, &y); e[i].x = y, e[i].y = x, e[i].n = i; }
    sort(e+1, e+1+m);
    now=n=0; nq=1; rep(i, 0, l-1) 
        if (s[i]!='P' && s[i]!='B') { now=r[now][s[i]-'a']; Add(ds[now], 1); }
        else if (s[i]=='P') Query(++n); else { Add(ds[now], -1); now = h[now]; }
    rep(i, 1, m) e[i].x = e[i].n; sort(e+1, e+1+m); rep(i, 1, m) printf("%d\n", e[i].y);
    return 0;
}
View Code

BZOJ 3172: [TJOI2013]单词

依旧FailTree搞,建好之后就遍历+统计,然后s[i]的出现次数就是末尾节点的Fail子树所包含的其他串的节点个数(重复出现也要算)。

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 1234567
#define MAX 1037471823

using namespace std;

struct node { int y, z; } e[MS]; int fir[MS];
int n, c[MS], k[MS], r[MS][26], f[MS], tc, ss[MS], x;
queue<int> q;
char s[MS];

void Add(int n)
{
    int o = 0, l = strlen(s);
    rep(i, 0, l-1) 
    {
        if (!r[o][s[i]-'a']) tc++, r[o][s[i]-'a'] = tc, c[tc] = s[i]-'a';
        o = r[o][s[i]-'a'], k[o]++;
    }
    ss[n] = o;
}

void DFS(int x)
{
    int o = fir[x];
    while (o)
        { DFS(e[o].y); k[x] += k[e[o].y]; o = e[o].z; }
}
    
int main()
{
    scanf("%d", &n);
    rep(i, 1, n) { scanf("%s", s); Add(i); }
    q.push(0); f[0] = -1;
    while (!q.empty())
    {
        x = q.front(); q.pop();
        rep(i, 0, 25) if (r[x][i])
        {
            int fo = f[x]; 
            while (fo >= 0 && !r[fo][i]) fo = f[fo];
            if (fo < 0) f[r[x][i]] = 0; else f[r[x][i]] = r[fo][i]; q.push(r[x][i]);
        }
    }
    rep(i, 1, tc) e[i].y = i, e[i].z = fir[f[i]], fir[f[i]] = i; DFS(0);
    rep(i, 1, n) printf("%d\n", k[ss[i]]);
    return 0; 
}
View Code

BZOJ 1030: [JSOI2007]文本生成器

数位DP+AC自动机,没了。

#include <queue>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 6789
#define MAX 1037471823
#define Q 10007

using namespace std;

struct node{ int y, n; }e[MS]; int fir[MS];
int n, m, c[MS], r[MS][26], f[MS], tc, dp[123][MS], ans, x, y;
bool b[MS]; char s[MS];
queue <int> q;

void Add()
{
    int l = strlen(s), o = 0;
    rep(i, 0, l-1)
        { if (!r[o][s[i]-'A']) tc++, r[o][s[i]-'A'] = tc, c[tc] = s[i]-'A'; o = r[o][s[i]-'A']; }
    b[o] = true;
}

void DFS(int x)
{
    int o = fir[x]; b[x] = true;
    while (o) { DFS(e[o].y); o = e[o].n; }
}

/* void Query(int x, int n)
{
    if (x == 1) rep(i, 0, tc) rep(j, 0, tc) dp[n][i][j] = k[i][j] % Q; 
    else if (x % 2==1) 
    {
        Query(x-1, n+1);
        rep(i, 0, tc) rep(j, 0, tc) rep(o, 0, tc) dp[n][i][j] += dp[n+1][i][o] * k[o][j], dp[n][i][j]%=Q;
    }
    else
    {
        Query(x/2, n+1);
        rep(i, 0, tc) rep(j, 0, tc) rep(o, 0, tc) dp[n][i][j] += dp[n+1][i][o] * dp[n+1][o][j], dp[n][i][j]%=Q;
    }
} */

int Mult(int x)
{
    if (x == 1) return 26; else if (x % 2 == 1) return (Mult(x-1)*26)%Q; else { int a = Mult(x/2); return (a*a)%Q; }
}

int main()
{
    scanf("%d%d", &n, &m);
    rep(i, 1, n) { scanf("%s", s); Add(); }
    q.push(0); f[0] = -1;
    while (!q.empty())
    {
        x = q.front(); q.pop();
        rep(i, 0, 25) if (r[x][i])
        {
            y = f[x];
            while (y >= 0 && !r[y][i]) y = f[y];
            if (y < 0) f[r[x][i]] = 0; else f[r[x][i]] = r[y][i]; q.push(r[x][i]);
        }
    }
    rep(i, 1, tc) e[i].y = i, e[i].n = fir[f[i]], fir[f[i]] = i;
    rep(i, 1, tc) if (b[i]) DFS(i);
    dp[0][0] = 1; 
    rep(k, 0, m-1)
        rep(i, 0, tc) if (!b[i] && dp[k][i]) 
        {
            rep(j, 0, 25) 
            {
                y = i; 
                while (y >= 0 && !r[y][j]) y = f[y];
                if (y < 0) dp[k+1][0]+=dp[k][i], dp[k+1][0]%=Q; else dp[k+1][r[y][j]]+=dp[k][i], dp[k+1][r[y][j]]%=Q;
            }
        }
    rep(i, 0, tc) if (!b[i]) ans = (ans + dp[m][i])% Q;
    /* Query(m, 1);
    rep(i, 0, tc) if (!b[i]) ans = (ans + dp[1][0][i])%Q; */
    ans = Mult(m) - ans; while (ans < 0) ans += Q;
    printf("%d\n", ans);
    return 0;
}
View Code

BZOJ 2754: [SCOI2012]喵星球上的点名

建AC自动机,然后对于每次询问就查找询问串的Fail子树所包含那些字符串的节点,然后被点到名字的就次数+1。

结果AC是AC了,时间跑出了个11s。。。

吐血。。。

那么我们换成后缀数组来做:

把所有出现过的串全部连成一串(中间插字符),然后后缀一遍,接着从上到下找,对于以询问串作为前缀的后缀s[i],我们就找出所有和s[i]的公共前缀长度大于询问串长度的后缀,然后统计去重。。。

嗯AC,2s

#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cmath>
#include <set>
#include <queue>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 345678
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, s;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, m, x, y, l, s[MS], k[MS], sa[MS], r[MS], ss[MS], c1[MS], c2[MS], h[MS], b[MS];

int main()
{
    scanf("%d%d", &n, &m);
    rep(i, 1, n*2) 
        { scanf("%d", &x); rep(j, 1, x) { scanf("%d", &s[++l]); k[l] = (i+1)/2; } s[++l] = -1; }
    rep(i, 1, m) 
        { k[l+1] = -i; scanf("%d", &ss[i]); rep(j, 1, ss[i]) scanf("%d", &s[++l]); s[++l] = -1; }
    x = 1; rep(i, 1, l) r[i] = s[i];
    while (x <= l)
    {
        rep(i, 1, l) q[i].x = r[i], q[i].y = i+x>l?0:r[i+x], q[i].s = i;
        sort(q+1, q+1+l);
        y = r[q[1].s] = 1;
        rep(i, 2, l) if ((q[i].x != q[i-1].x) || (q[i].y != q[i-1].y)) r[q[i].s] = ++y; else r[q[i].s] = y;
        if (y == l) break; else x *= 2;
    }
    rep(i, 1, l) sa[r[i]] = i;
    x = 0; rep(i, 1, l)
    {
        if (x) x--;
        int j = sa[r[i]-1];
        while (s[i+x]==s[j+x]) x++;
        h[r[i]] = x;
    }
    rep(i, 1, l) if (k[i] < 0)
    {
        y = -k[i];
        x = r[i]; 
        while (h[x] >= ss[y]) 
        { 
            if (k[sa[x-1]] > 0 && b[k[sa[x-1]]] != y) 
                b[k[sa[x-1]]] = y, c1[k[sa[x-1]]]++, c2[y]++; 
            x--; 
        }
        x = r[i]+1; 
        while (h[x] >= ss[y]) 
        { 
            if (k[sa[x]] > 0 && b[k[sa[x]]] != y) 
                b[k[sa[x]]] = y, c1[k[sa[x]]]++, c2[y]++; 
            x++; 
        }
    }
    rep(i, 1, m) printf("%d\n", c2[i]);
    rep(i, 1, n) printf("%d%c", c1[i], i==n?'\n':' ');
    return 0;
}
View Code

 


 

然后Ecstasy大神扔了份后缀数组论文的pdf给我。。。

POJ 1743: Musical Theme

求不重叠·最长·重复·子串的长度。

二分答案+分组统计(后缀数组最重要最常用的思想),判断组内Sa的极差。

#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 23456
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, s[MS], h[MS], sa[MS], rk[MS], a, p, l, r;

bool Check(int k)
{
    int bs = 0, ss = 0;
    rep(i, 1, n) 
        if (h[i] < k) if (bs - ss >= k) return true; else bs = ss = sa[i]; else
        { if (ss > sa[i]) ss = sa[i]; if (bs < sa[i]) bs = sa[i]; }
    if (bs - ss >= k) return true; else return false; 
}

int main()
{
    scanf("%d", &n);
    while (n)
    {
        rep(i, 1, n) scanf("%d", &s[i]);
        rep(i, 1, n-1) s[i] = s[i+1] - s[i]; n--; 
        rep(i, 1, n) rk[i] = s[i];
        p = 1; while (p <= n)
        {
            rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; 
            sort(q+1, q+1+n);
            a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; 
                else rk[q[i].z] = a;
            if (a == n) break; else p*=2;
        }
        rep(i, 1, n) sa[rk[i]] = i;
        a = 0; rep(i, 1, n)
        {
            if (a) a--; p = sa[rk[i]-1];
            while (s[i+a] == s[p+a]) a++; h[rk[i]] = a;
        }
        l = 0, r = n/2; while (l != r) if (Check((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2;
        if (l < 4) printf("0\n"); else printf("%d\n", l+1); scanf("%d", &n);
    }
    return 0;
}
View Code

POJ 3261: Milk Patterns

求可重叠·最长·至少重复K次·子串的长度。

依旧二分答案+分组统计,判断组内后缀的个数。

#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 23456
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, s[MS], h[MS], sa[MS], rk[MS], a, p, l, r, c;

bool Check(int k)
{
    int a = 0;
    rep(i, 1, n+1) if (h[i] < k) { if (i - a >= c) return true; else a = i; }
    return false;
}

int main()
{
    scanf("%d%d", &n, &c);
    rep(i, 1, n) scanf("%d", &s[i]);
    rep(i, 1, n) rk[i] = s[i];
    p = 1; while (p <= n)
    {
        rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
        a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
        if (a == n) break; else p*=2;
    }
    rep(i, 1, n) sa[rk[i]] = i;
    a = 0; rep(i, 1, n)
    {
        if (a) a--; p = sa[rk[i]-1];
        while (s[p+a] == s[i+a]) a++; h[rk[i]] = a;
    }
    l = 0, r = n+1; while (l!=r) if (Check((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2;
    printf("%d\n", l);
    return 0;
}
View Code

SPOJ DISUBSTR: Distinct Substrings

求不相同·子串的个数。

Ans=∑(n-sa[k]+1-height[k])(1..k..n)

#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 2345
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int t, n, sa[MS], rk[MS], h[MS], a, p, ans;
char s[MS];

int main()
{
    scanf("%d", &t);
    while (t--)
    {
        scanf("%s", s); n = strlen(s);
        rep(i, 1, n) rk[i] = s[i-1];
        p = 1; while (p <= n)
        {
            rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i; sort(q+1, q+1+n);
            a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
            if (a == n) break; else p*=2;
        }
        rep(i, 1, n) sa[rk[i]] = i;
        a = 0; rep(i, 1, n)
        {
            if (a) a--; p = sa[rk[i]-1];
            while (s[p+a-1] == s[i+a-1]) a++; h[i] = a;
        }
        rep(i, 1, n) ans -= h[i];
        ans += (n*n+n)/2; printf("%d\n", ans); ans = 0;
    }
    return 0;
}
View Code

SPOJ SUBST1: New Distinct Substrings

和上一道一样咯

#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 56789
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int t, n, sa[MS], rk[MS], a, p;
long long ans;
char s[MS];

int main()
{
    scanf("%d", &t);
    while (t--)
    {
        scanf("%s", s); n = strlen(s); ans = n; ans = (ans*n+n)/2;
        rep(i, 1, n) rk[i] = s[i-1];
        p = 1; while (p <= n)
        {
            rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i; sort(q+1, q+1+n);
            a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
            if (a == n) break; else p*=2;
        }
        rep(i, 1, n) sa[rk[i]] = i;
        a = 0; rep(i, 1, n)
        {
            if (a) a--; p = sa[rk[i]-1];
            while (s[p+a-1] == s[i+a-1]) a++; ans -= a;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
View Code

Ural 1297: Palindrome

最长回文子串,经典!

翻转连接后就变成了求最长公共子串。

#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 2345
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int t, n, sa[MS], rk[MS], h[MS], k[MS][19], a, p, ans, ans2, x;
char s[MS];

int rmq(int x, int y)
{
    if (x > y) a = x, x = y, y = a; x++;
    p = int(log(y-x+1)/log(2)); a = 1; rep(i, 1, p) a *= 2; 
    return min(k[x][p], k[y-a+1][p]);
}

int main()
{
    scanf("%s", s); n = strlen(s);
    s[n] = ' '; rep(i, 1, n) s[i+n] = s[n-i]; n = n*2+1;
    rep(i, 1, n) rk[i] = s[i-1];
    p = 1; while (p <= n)
    {
        rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i; sort(q+1, q+1+n);
        a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
        if (a == n) break; else p*=2;
    }
    rep(i, 1, n) sa[rk[i]] = i;
    a = 0; rep(i, 1, n)
    {
        if (a) a--; p = sa[rk[i]-1];
        while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
    }
    rep(i, 1, n) k[i][0] = h[i];
    a = 1; rep(j, 1, int(log(n)/log(2)))
        { rep(i, 1, n-a) k[i][j] = min(k[i][j-1], k[i+a][j-1]); a*=2; }
    rep(i, 1, n/2) 
        { x = rmq(rk[i], rk[n-i+1]); if (ans < x*2-1) ans = x*2-1, ans2 = i-x+1; }
    rep(i, 2, n/2) 
        { x = rmq(rk[i], rk[n-i+2]); if (ans < x*2) ans = x*2, ans2 = i-x; }
    rep(i, ans2, ans2+ans-1) printf("%c", s[i-1]); printf("\n");
    return 0;
}
View Code

POJ 2406: Power Strings

给定一个字符串L,已知这个字符串是由某个字符串S重复R次而得到的,求R的最大值。

从小到大枚举S的长度K,然后判断L能否整除K,且Suf[1]与Suf[1+k]的公共前缀长度是否等于L-k

#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 1234567
#define MAX 1073741823

using namespace std;

int n, f[MS], a;
char s[MS];

int main()
{
    scanf("%s", s);
    while (s[0] != '.')
    {
        n = strlen(s);
        f[0] = -1; rep(i, 2, n)
        {
            a = f[i-1]; while (a >= 0 && s[a] != s[i-1]) a = f[a];
            if (a < 0) f[i] = 0; else f[i] = a+1;
        }
        if (n%(n-f[n])==0) printf("%d\n", n/(n-f[n])); else printf("1\n"); scanf("%s", s);
    }
    return 0;
}
View Code

POJ 3693: Maximum repetition substring

给定一个字符串,求重复次数最多的连续重复子串。

这题还是得看论文的解释。。。我这题貌似弄最久。。。WA了7次

#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 123456
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, rk[MS], sa[MS], h[MS], k[MS][20], rec[MS], o, m, a, p, l, T, t, ans, pd;
char s[MS];

int rmq(int x, int y)
{
    int a, p;
    if (x > y) a = x, x = y, y = a; x++;
    a = 1, p = 0; while (a*2 <= y-x+1) a*=2, p++; 
    return min(k[x][p], k[y-a+1][p]);
}

int main()
{
    scanf("%s", s);
    while (s[0]!='#')
    {
        n = strlen(s);
        rep(i, 1, n) rk[i] = s[i-1];
        p = 1; while (p <= n)
        {
            rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
            a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
            if (a == n) break; else p *= 2;
        }
        rep(i, 1, n) sa[rk[i]] = i;
        a = 0; rep(i, 1, n)
        {
            if (a) a--; p = sa[rk[i]-1];
            while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a; 
        }
        rep(i, 1, n) k[i][0] = h[i];
        a = 1, p = 0; while (a*2 <= n) a*=2, p++;
        a = 1; rep(j, 1, p)
            { rep(i, 1, n-a) k[i][j] = min(k[i][j-1], k[i+a][j-1]); a*=2; }
        ans = 1;
        rep(l, 1, n-1)
        {
            a = 1;
            while (a+l <= n) if (s[a-1] == s[a+l-1])
            {
                m = rmq(rk[a], rk[a+l]); 
                p = m/l+1, t = a-(l-m%l); 
                if (t>0 && m%l!=0 && rmq(rk[t], rk[t+l])>=m) p++;
                if (p > ans) ans = p, o = 1, rec[o] = l;
                else if (p == ans && rec[o] != l) rec[++o] = l;
                a += l;
            } else a+=l;
        }
        if (ans == 1) { o = n-1; rep(i, 1, o) rec[i] = i; }
        pd = 0;
        rep(i, 1, n) if (!pd) rep(j, 1, o)
            { if (rmq(i, rk[sa[i]+rec[j]]) >= (ans-1)*rec[j]) { a = sa[i]; p = rec[j]; pd++; break; } }
        else break;
        printf("Case %d: ", ++T);
        rep(i, a, a+p*ans-1) printf("%c", s[i-1]); printf("\n");
        scanf("%s", s);
    }
    return 0;
}
View Code

SPOJ REPEATS: Repeats

和上一道一样咯

#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 56789
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, rk[MS], sa[MS], h[MS], k[MS][20], m, a, p, l, T, t, ans, pd;
char s[MS];

int rmq(int x, int y)
{
    int a, p;
    if (x > y) a = x, x = y, y = a; x++;
    a = 1, p = 0; while (a*2 <= y-x+1) a*=2, p++; 
    return min(k[x][p], k[y-a+1][p]);
}

int main()
{
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        rep(i, 1, n) { s[i-1] = 0; while (s[i-1] < 'a' || s[i-1] > 'z') scanf("%c", &s[i-1]); }
        rep(i, 1, n) rk[i] = s[i-1];
        p = 1; while (p <= n)
        {
            rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
            a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
            if (a == n) break; else p *= 2;
        }
        rep(i, 1, n) sa[rk[i]] = i;
        a = 0; rep(i, 1, n)
        {
            if (a) a--; p = sa[rk[i]-1];
            while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a; 
        }
        rep(i, 1, n) k[i][0] = h[i];
        a = 1, p = 0; while (a*2 <= n) a*=2, p++;
        a = 1; rep(j, 1, p)
            { rep(i, 1, n-a) k[i][j] = min(k[i][j-1], k[i+a][j-1]); a*=2; }
        ans = 1;
        rep(l, 1, n-1)
        {
            a = 1;
            while (a+l <= n) if (s[a-1] == s[a+l-1])
            {
                m = rmq(rk[a], rk[a+l]); 
                p = m/l+1, t = a-(l-m%l); 
                if (t>0 && m%l!=0 && rmq(rk[t], rk[t+l])>=m) p++;
                if (p > ans) ans = p;
                a += l;
            } else a += l;
        }
        printf("%d\n", ans);
    }
    return 0;
}
View Code

Ural 1517: Freedom of Choice

最长公共子串。连起来,没了。

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <cmath>
#include <algorithm>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 234567
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x<k.x || (x==k.x && y<k.y); }
} q[MS];
int n, l, a, p, rk[MS], sa[MS], h[MS], ans;
char s[MS], s1[MS];

int main()
{
    scanf("%d", &l);
    scanf("%s", s1); rep(i, 1, l) s[i-1] = s1[i-1]; s[l] = ' '; n = l+1;
    scanf("%s", s1); rep(i, 1, l) s[n+i-1] = s1[i-1]; n += l;
    rep(i, 1, n) rk[i] = s[i-1];
    p = 1; while (p <= n)
    {
        rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
        a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
        if (a == n) break; else p *= 2;
    }
    rep(i, 1, n) sa[rk[i]] = i;
    a = 0; rep(i, 1, n)
    {
        if (a) a--; p = sa[rk[i]-1];
        while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
    }
    l = sa[1]; rep(i, 3, n) if ((sa[i-1] < l && sa[i] > l) || (sa[i-1] > l && sa[i] < l)) ans = max(ans, h[i]);
    rep(i, 3, n) if (((sa[i-1] < l && sa[i] > l) || (sa[i-1] > l && sa[i] < l)) && ans == h[i]) 
    {
        rep(j, 1, ans) printf("%c", s[sa[i]+j-2]);
        return 0;
    }
    return 0;
}
View Code

POJ 2774: Long Long Message

也是最长公共子串。

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <cmath>
#include <algorithm>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 234567
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x<k.x || (x==k.x && y<k.y); }
} q[MS];
int n, l, a, p, rk[MS], sa[MS], h[MS], ans;
char s[MS], s1[MS];

int main()
{
    scanf("%s", s1); l = strlen(s1); 
    rep(i, 1, l) s[i-1] = s1[i-1]; s[l] = ' '; n = l+1;
    scanf("%s", s1); l = strlen(s1);
    rep(i, 1, l) s[n+i-1] = s1[i-1]; n += l;
    rep(i, 1, n) rk[i] = s[i-1];
    p = 1; while (p <= n)
    {
        rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
        a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
        if (a == n) break; else p *= 2;
    }
    rep(i, 1, n) sa[rk[i]] = i;
    a = 0; rep(i, 1, n)
    {
        if (a) a--; p = sa[rk[i]-1];
        while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
    }
    l = sa[1]; rep(i, 3, n) if ((sa[i-1] < l && sa[i] > l) || (sa[i-1] > l && sa[i] < l)) ans = max(ans, h[i]);
    printf("%d", ans);
    return 0;
}
View Code

POJ 3415: Common Substrings

从上到下遍历Height,分组讨论。对于每个组,答案加上组内来自不同串的后缀两两之间的公共前缀长度之和。这一步得用单调栈维护才能够比较快。。。

#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 234567
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, k, l, la, rk[MS], sa[MS], h[MS], a, p, b;
long long ans, as, bs;
char s[MS], s1[MS];
int am, bm;
struct node2 {int x, y;} ac[MS], bc[MS];

int main()
{
    scanf("%d", &k);
    while (k)
    {
        scanf("%s", s1); l = strlen(s1); rep(i, 1, l) s[i-1] = s1[i-1]; s[l] = ' '; n = la = l+1;
        scanf("%s", s1); l = strlen(s1); rep(i, 1, l) s[n+i-1] = s1[i-1]; n += l;
        rep(i, 1, n) rk[i] = s[i-1];
        p = 1; while (p <= n)
        {
            rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
            a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
            if (a == n) break; else p *= 2;
        }
        rep(i, 1, n) sa[rk[i]] = i;
        a = 0; rep(i, 1, n)
        {
            if (a) a--; p = sa[rk[i]-1];
            while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a; 
        } 
        ans = am = bm = h[1] = 0;
        rep(i, 1, n) h[i] += 1-k;
        rep(i, 1, n)
        {
            if (h[i] < 1) am = bm = as = bs = 0;
            a = am; while (a && ac[a].x > h[i]) 
            {
                as -= (ac[a].x - h[i])*ac[a].y, ac[a].x = h[i]; if (a != am) ac[a].y += ac[a+1].y, am--;
                a--;
            }
            b = bm; while (b && bc[b].x > h[i]) 
            {
                bs -= (bc[b].x - h[i])*bc[b].y, bc[b].x = h[i]; if (b != bm) bc[b].y += bc[b+1].y, bm--;
                b--;
            } 
            if (sa[i] > la) 
                bc[++bm].x = MAX, bc[bm].y = 1, bs += MAX, ans += as;
            else
                ac[++am].x = MAX, ac[am].y = 1, as += MAX, ans += bs;
        }
        printf("%lld\n", ans); scanf("%d", &k);
    }
    return 0;
}
View Code

SPOJ PHRASES: Relevant Phrases of Annihilation

#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstdio>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 123456
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, m, l, r, a, p, rk[MS], sa[MS], h[MS], b[MS], c[MS], d[MS];
char s1[MS], s[MS];

bool Query(int k)
{
    a = p = 0;
    rep(i, 1, n) 
    {
        if (h[i] < k) a = 0, p++;
        if (b[sa[i]] && c[b[sa[i]]] != p) c[b[sa[i]]] = p, a++;
        if (a > m/2) return true;
    }
    return false;
}

int main()
{
    scanf("%d", &m);
    while (m)
    {
        n = 0, r = MAX;
        rep(i, 1, m) 
        {
            scanf("%s", s1); l = strlen(s1); r = min(r, l);
            rep(j, 1, l) s[n+j-1] = s1[j-1], b[n+j] = i, d[n+j] = l-j+1; if (i<m) s[n+l] = ' ', n += l+1; else n += l;
        }
        rep(i, 1, n) rk[i] = s[i-1];
        p = 1; while (p <= n)
        {
            rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i;
            sort(q+1, q+1+n);
            a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
            if (a == n) break; else p *= 2;
        }
        rep(i, 1, n) sa[rk[i]] = i;
        a = 0; rep(i, 1, n)
        {
            if (a) a--; p = sa[rk[i]-1];
            while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
        } 
        h[1] = 0; rep(i, 2, n) if (min(d[sa[i-1]], d[sa[i]]) < h[i]) h[i] = min(d[sa[i-1]], d[sa[i]]);
        l = 0;
        while (l != r)
            if (Query((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2;
        if (!l) printf("?\n"); else
        {
            a = p = 0;
            rep(i, 1, n) 
            {
                if (h[i] < l) a = 0, p++;
                if (b[sa[i]] && c[b[sa[i]]] != p) c[b[sa[i]]] = p, a++;
                if (a == m/2+1) { a = -MAX; rep(j, sa[i]-1, sa[i]+l-2) printf("%c", s[j]); printf("\n");  }
            }
        }
        printf("\n"); scanf("%d", &m);
    }
    return 0;
}
View Code

POJ 3294: Life Forms

#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstdio>

#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 123456
#define MAX 1073741823

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, m, l, r, a, p, rk[MS], sa[MS], h[MS], b[MS], c[MS], d[MS];
char s1[MS], s[MS];

bool Query(int k)
{
    a = p = 0;
    rep(i, 1, n) 
    {
        if (h[i] < k) a = 0, p++;
        if (b[sa[i]] && c[b[sa[i]]] != p) c[b[sa[i]]] = p, a++;
        if (a > m/2) return true;
    }
    return false;
}

int main()
{
    scanf("%d", &m);
    while (m)
    {
        n = 0, r = MAX;
        rep(i, 1, m) 
        {
            scanf("%s", s1); l = strlen(s1); r = min(r, l);
            rep(j, 1, l) s[n+j-1] = s1[j-1], b[n+j] = i, d[n+j] = l-j+1; if (i<m) s[n+l] = ' ', n += l+1; else n += l;
        }
        rep(i, 1, n) rk[i] = s[i-1];
        p = 1; while (p <= n)
        {
            rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i;
            sort(q+1, q+1+n);
            a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
            if (a == n) break; else p *= 2;
        }
        rep(i, 1, n) sa[rk[i]] = i;
        a = 0; rep(i, 1, n)
        {
            if (a) a--; p = sa[rk[i]-1];
            while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
        } 
        h[1] = 0; rep(i, 2, n) if (min(d[sa[i-1]], d[sa[i]]) < h[i]) h[i] = min(d[sa[i-1]], d[sa[i]]);
        l = 0;
        while (l != r)
            if (Query((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2;
        if (!l) printf("?\n"); else
        {
            a = p = 0;
            rep(i, 1, n) 
            {
                if (h[i] < l) a = 0, p++;
                if (b[sa[i]] && c[b[sa[i]]] != p) c[b[sa[i]]] = p, a++;
                if (a == m/2+1) { a = -MAX; rep(j, sa[i]-1, sa[i]+l-2) printf("%c", s[j]); printf("\n");  }
            }
        }
        printf("\n"); scanf("%d", &m);
    }
    return 0;
}
View Code

POJ 1226: Substrings

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <fstream>

#define MS 123456
#define MAX 1<<30
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)

using namespace std;

struct node
{
    int x, y, z;
    bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, T, m, a, p, l, r, rk[MS], sa[MS], h[MS], b[MS], d[MS], c[MS];
char s1[MS], s[MS];

bool Query(int k)
{
    a = p = 0; rep(i, 1, m) c[i] = 0;
    rep(i, 1, n)
    {
        if (h[i] < k) a = 0, p++;
        if (b[sa[i]] && p != c[b[sa[i]]]) c[b[sa[i]]] = p, a++;
        if (a == m) return true;
    }
    return false;
}

int main()
{
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &m); n = 0, r = MAX;
        rep(i, 1, m)
        {
            scanf("%s", s1); l = strlen(s1); r = min(l, r);
            rep(j, 1, l) s[n+j-1] = s1[j-1], b[n+j] = i, d[n+j] = l-j+1; n += l+1; s[n-1] = ' ', b[n] = 0, d[n] = 0;
            rep(j, 1, l) s[n+j-1] = s1[l-j], b[n+j] = i, d[n+j] = l-j+1; n += l+1; s[n-1] = ' ', b[n] = 0, d[n] = 0;
        }
        n--; rep(i, 1, n) rk[i] = s[i-1];
        p = 1; while (p <= n)
        {
            rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i;
            sort(q+1, q+1+n);
            a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
            if (a == n) break; else p *= 2;
        }
        rep(i, 1, n) sa[rk[i]] = i;
        a = 0; rep(i, 1, n)
        {
            if (a) a--; p = sa[rk[i]-1];
            while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
        }
        h[1] = 0; rep(i, 2, n) if (min(d[sa[i-1]], d[sa[i]]) < h[i]) h[i] = min(d[sa[i-1]], d[sa[i]]);
        l = 0; while (l != r) if (Query((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2;
        printf("%d\n", l);
    }
    return 0;
}
View Code

上面三题都是二分答案+分组讨论,也没什么好说了。

 

总计18道。。。

累。。。

posted @ 2014-12-28 19:36  NanoApe  阅读(410)  评论(1编辑  收藏  举报
AmazingCounters.com