• 字符串模式匹配:POJ 3461 Oulipo


    这道题是字符串的模式匹配,要求计算出模式串在文本串中出现的次数,比如:"AZA" 在 "AZAZAZA" 中出现了 3 次;

    这道题使用 KMP 过的,但是 horspool 却不能过,尝试了一下各种方法后,还是回到了麻烦的 KMP,留下了以下几个模版:

    1. shift-or,据说比 KMP 快两倍,但只适用于模式串在 32 位以内,64位以内的也可以,需要把 b[] 改为 long long int 型,超出范围的可以尝试自己构造长类型……这道题不适用

    # include <string.h>
    
    int b[128];                     /* int型共32位,模式串不超过32位 */
    char p[32], t[1000005];
    
    int shift_or(char *p, char *t)
    {
        int n, m, i, d, f, ret;
    
        ret = 0;
        
        n = strlen(p);
        m = strlen(t);
        
        memset(b, 0xff, sizeof(b));
        
        for (i = 0; i < n; ++i)
            b[p[i]] &= ~(0x1<<i);
        
        d = 0xff;
        f = 0x1 << (n-1);
        for (i = 0; i < m; ++i)
        {
            d = (d<<1) | b[t[i]];     
            if (!(d&f)) ++ret;
        }
        
        return ret;
    }

    2. horspool,基于后缀搜索,据说比 KMP 快,但是这道题 TLE 了

    # include <stdio.h>
    # include <string.h>
    
    int d[128];
    char p[10005], t[1000005];
    
    int horspool(char *p, char *t);
    
    int main()
    {
        int T;
            
        p[0] = t[0] = '*';   /* 数组下标改为从1开始,方便不少 */
        scanf("%d", &T);
        while (T--)
        {
            scanf("%s%s", p+1, t+1);
            printf("%d\n", horspool(p, t));
        }
        
        return 0;
    }
    
    int horspool(char *p, char *t)
    {
        int m, n, i, ret, ptr;
        
        ret = 0;
        
        m = strlen(p) - 1;
        n = strlen(t) - 1;
        
        for (i = 'A'; i <= 'Z'; ++i) d[i] = m;  /* i 循环是对整个字母表 */
        for (i = 1; i <= m-1; ++i) d[p[i]] = m - i;
        
        ptr = 0;
        while (ptr <= n - m)
        {
            i = m;
            while (i > 0 && t[ptr+i] == p[i]) --i;
            if (i == 0) ++ret;
            ptr = ptr + d[t[ptr+m]];
        }
        
        return ret;
    }

    3. 最省事的 strstr, 当然会 TLE 了

    # include <string.h>
    
    char p[10005], t[1000005];
    
    int strstr_mat(char *p, char *t)
    {
        int ret;
        char *ptr;
        
        ret = 0;
        while (ptr = strstr(t, p))
        {
            ++ret;
            t += ptr-t+1;
        }
        
        return ret;
    }

    4. 最后是 KMP,AC用的这个,80MS,这个算法我总是理解着很困难,且理解后很快就忘了……,对照书上的伪代码比较好写

    # include <stdio.h>
    # include <string.h>
    
    short pi[10005];  /* 这道题 short 够用 */
    char p[10005], t[1000005];
    
    int kmp(char *p, char *t);
    void comp_prefix(char *p, int plen);
    
    int main()
    {
        int T;
        
        p[0] = t[0] = '*';  /* 同样是为了方便,将下标改为从1开始 */
        scanf("%d", &T);
        while (T--)
        {
            scanf("%s%s", p+1, t+1);
            printf("%d\n", kmp(p, t));
        }
        
        return 0;
    }
    
    int kmp(char *p, char *t)
    {
        int n, m, i, q, ret;
        
        ret = 0;
        
        m = strlen(p) - 1;
        n = strlen(t) - 1;
        
        comp_prefix(p, m);
        
        q = 0;
        for (i = 1; i <= n; ++i)
        {
            while (q > 0 && p[q+1] != t[i]) q = pi[q];
            if (p[q+1] == t[i]) ++q;
            if (q == m)
            {
                ++ret;
                q = pi[q];
            }
        }
        
        return ret;
    }
    
    void comp_prefix(char *p, int plen)
    {
        int q, k;
        
        pi[1] = 0;
        k = 0;
        for (q = 2; q <= plen; ++q)
        {
            while (k > 0 && p[k+1] != p[q]) k = pi[k];
            if (p[k+1] == p[q]) ++k;
            pi[q] = k;
        }
    }

    shift-or 的扩展或许更有效,没有试,本来比 kmp 要简单很多,加上扩展就显得太麻烦了。

  • 相关阅读:
    说说与线程相关的方法
    sleep()和yield()有什么区别?
    同步和异步有何异同,分别在什么情况下使用?
    如何保证多个线程同时启动?
    volatile关键字能否保证线程安全?
    使用对象的wait()方法需要注意什么?
    乐观锁与悲观锁是什么?
    Condition实现等待、唤醒
    LongAdder与AtomicLong有什么区别?
    介绍一下ForkJoinPool的使用
  • 原文地址:https://www.cnblogs.com/JMDWQ/p/2495548.html
Copyright © 2020-2023  润新知