http://acm.zzuli.edu.cn/zzuliacm/problem.php?cid=1158&pid=5 二分函数的间接应用

Description

小火山获得了一个字符串,然而大火山让小火山从里面截取一段字符串,并且让小火山截取的字符串满足一些字符达到一定数量。

小火山觉得很容易,但是他想要知道他至少得截取多长的字符串。

Input

首先是一个整数t(t<=100),表示测试数据组数。接下来是两个整数n和m(n<=10000, m<=10),n表示字符串的长度,m表示要满足一定数量的字符

的种类.(字符只包含小写英文字母)

个数(没有重复字符种类),然后有m行,每行第一个是一个字符,然后是一个整数x(x<=50),表示这种字符的的要求数量。

Output

输出最小长度,如果达不到要求输出-1

Sample Input

1

6 3

cancan

c 2

a 2

n 2

Sample Output

6

一道很好的二分函数的直接应用 因为设计了多个单词的查找 变比一般的难上一点

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<math.h>
#define LL long long
using namespace std;
#define INF 0x3f3f3f3f
#define N 10009
char str1[N];
int qq[30][N];
struct node
{
    int w,a,ww;
    char str[12];
}q[N];
int main()
{
    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
        memset(q,0,sizeof(q));
        scanf("%d%d%s",&n,&m,str1);
        for(int i=0;i<m;i++)
        {
            scanf("%s%d",q[i].str,&q[i].a);
            q[i].w=q[i].str[0]-'a';////q[i].w主要是转换成数字 ,方便下面的应用
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++) ///qq[q[j].w][i]按照递增,方便二分查找
            {
                if(str1[i]==q[j].str[0]) ///统计q[j].ww ,比较a[i]  提前判断-1的情况
                    q[j].ww++;
                if(i==0)
                {
                    if(str1[i]==q[j].str[0]) qq[q[j].w][i]=1;
                    else qq[q[j].w][i]=0;
                }
                else if(str1[i]==q[j].str[0])
                    qq[q[j].w][i]=qq[q[j].w][i-1]+1;
                else
                    qq[q[j].w][i]=qq[q[j].w][i-1];
            }
        }
        int s=0;
        for(int i=0;i<m;i++)
            if(q[i].ww<q[i].a)
            s=1;
        if(s==1)///特殊情况的输出
        {
            printf("-1\n");
            continue;
        }
        int anss=INF,ans;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(str1[i]==q[j].str[0])
                {
                    int e=i,ss=0;
                    if(qq[q[j].w][n-1]-q[j].a+1<qq[q[j].w][i])///判断后面是否还够所需要的个数
                    {
                        ss=1;
                        break;
                    }
                    ans=lower_bound(qq[q[j].w],qq[q[j].w]+n,qq[q[j].w][i]+q[j].a-1)-qq[q[j].w];///利用二分查找,已知当前出现是第几个,在往后找第所需要的个数
                    for(int k=j+1;k<m+j;k++)///利用for把所需要的个数全部遍历,在其中找到最近的也是最优的
                    {
                        int kk=k%m;
                        if(qq[q[kk].w][n-1]-q[kk].a<qq[q[kk].w][i])///判断后面是否还够所需要的个数
                        {
                            ss=1;
                             break;
                        }
                        ans=max(ans,lower_bound(qq[q[kk].w],qq[q[kk].w]+n,q[kk].a+qq[q[kk].w][i])-qq[q[kk].w]);///同上
                    }
                    if(ss==0)
                    anss=min(anss,ans-e+1);
                }
            }
        }
        printf("%d\n",anss);
    }
    return 0;
}