P1381 单词背诵

优美的尺取法!!!


这道题看到判断相同的字符串,用pp想都知道用hash。

其实重点在第二问:如何用较小的时间复杂度来实现题目中的最小距离包含最多要背的单词

这个时候就需要找到一种算法,这种算法名字叫做尺取法

别人的博客偷来三张图:

Center

Center

Center

算法核心思想就是上面写的那样:

  1. 初始化左顶点、右顶点和区间内还不满足要求的数目。

  2. 套一个while(1)的循环,这里从左端点开始枚举,所以当l或r等于m + 1时自然退出。

  3. 当区间满足要求时,试着缩小左端点。

  4. 当区间不满足要求时,从右端点一个一个地扩展。

看不懂是正常的,你可以照着下面的代码打一遍,然后开debug看一下就知道了。

更新答案的时候应该更新\(r-l\),因为这个算法是左闭右开的。

复杂度十分优美\(O(n)\)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 100005, maxm = 100005;
const int base = 137;
const int mod = 19260817;
int n, m;
int a[maxn];
int b[maxm];
bool need[mod];
int vis[mod];
int ans1, ans2;
int hashe(char *ch)
{
    long long ans = 0, len = strlen(ch);
    for(int i = 0; i < len; i++)
    {
        ans = (ans * base + ch[i]) % mod;
    }
    return (int)ans;
}
int main()
{
    scanf("%d", &n);
    char temp[20];
    for(int i = 1; i <= n; i++)
    {
        scanf("%s", temp);
        //printf("%d\n", hashe(temp));
        int has = hashe(temp);
        need[has] = true;
        a[i] = has;
    }
    scanf("%d", &m);
    for(int i = 1; i <= m; i++)
    {
        scanf("%s", temp);
        //printf("%d\n", hashe(temp));
        int has = hashe(temp);
        b[i] = has;
        if(need[b[i]] && !vis[b[i]]) vis[b[i]] = true, ans1++;
    }
    if(ans1 == 0)
    {
        printf("0\n0\n");
        return 0;
    }
    printf("%d\n", ans1);
    
    ans2 = 1 << 30;
    int l = 1, r = 1, cnt = ans1;
    memset(vis, 0, sizeof(vis));
    while(233)
    {
        if(!cnt)
        {
            while(!need[b[l]]) l++;
            if(l == m + 1) break;
            ans2 = std::min(ans2, r - l);
            if(vis[b[l]] == 1) cnt++;
            if(vis[b[l]] >= 1) vis[b[l]]--, l++;
        }
        else
        {
            if(r == m + 1) break;
            if(need[b[r]])
            {
                if(!vis[b[r]]) cnt--;
                vis[b[r]]++;
            }
            r++;
        }
    }
    printf("%d\n", ans2);
    return 0;
}

转载于:https://www.cnblogs.com/Garen-Wang/p/9467647.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值