A CodeForces 1187B

题意:

商家给出字符串\(S\),对于每个名字\(t_i\),需要回答至少需要从前往后多少个连续字符才能写出名字\(t_i\)。

Sol:

桶或者二分答案+前缀和

此处只讲解桶。

对\(S\),维护\(cnt[x][y]\)表示字母\(x\)出现了\(y\)次所在的位置。
那么显然对于询问来说,\(ans=max(cnt['a'...'z'][b['a'...'z']],ans)\),\(b[x]\)表示字母\(x\)在询问串中出现的次数。

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read() {
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0',c=getchar();}
    return x*f;
}
const int N=2e5+5;
map<int,int>Q[200];
map<int,int>cnt,ccnt;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin>>t;
	string s;
	cin>>s;
	for(int i=0;i<s.length();++i){
		cnt[s[i]]++;
		Q[s[i]][cnt[s[i]]]=i;
	} 
	int m;
	cin>>m;
	while(m--){
		cin>>s;
		int maxx=-0x3f3f3f3f;
		for(int i=0;i<s.length();++i){
			ccnt[s[i]]++;
			maxx=max(maxx,Q[s[i]][ccnt[s[i]]]);
		}
		ccnt.clear();
		cout<<maxx+1<<"\n";
	}
	return 0;
}

B CodeForces 1559B

题意:

把所有的 ? 替换成 R 或 B 使得相邻的两个字符不同。如果两个相邻格子的颜色相同,那么它就是不美观的,你要尽量减少不美观的数量。

Sol:

贪心。考虑对于一连串的\(?\),统一往右对齐,例如\(???B\),就改成\(RBRB\)即可,特殊处理最后一段末尾的\(?\)串,例如\(B???\),将其特殊处理一下,因为其右边没有字符,无法对齐。

搞法很多,能过就行。

Code:

#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<#x<<" :"<<x<<endl
typedef long long ll;
const ll mod=1e9+7;
const int N=2e5+10;
#define rep(i,a,b) for(int i=a;i<=b;++i)
ll qpow(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1){
            ans=(ans*a)%mod;
        }
        a=(a*a)%mod;
        b>>=1;
    }
    return ans;
}
typedef double db;
int m,n;
char s[N];
int main(){
    //freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n*2;++i)s[i]=' ';
        scanf("%s",s+1);
        int l=0;
        for(int i=1;i<=n;++i){
            int kep=i;
            int f=0;
            int cnt=0;
            while(s[kep]=='?'&&kep<=n){
                kep++;
                f=1;
                cnt++;
            }
            if(f){
                int fs=(s[kep]=='B'?0:1);
                if(kep!=n+1){
                    for(int j=kep-1;j>=i;--j){
                        fs^=1;
                        if(fs)s[j]='R';
                        else s[j]='B';
                    }
                    i=kep-1;
                }
                else{
                    fs=(s[i-1]=='B'?0:1);
                    for(int j=i;j<=kep-1;++j){
                        fs^=1;
                        if(fs)s[j]='R';
                        else s[j]='B';
                    }
                    i=kep-1;
                }
            }
        }
        printf("%s\n",s+1);
    }
    return 0;
}

C HDU 6955

题意:

给你一段长度为\(N\)的序列,和一个整数\(K\)。寻找左端点最小且区间长度最短的区间使满足\([L,R]\)的异或值\(>=K\)。输出\(L,R\)。不存在输出\(-1\).

Sol:

\(Trie\).
先做一遍异或前缀和,问题就转换成从异或前缀和操作后的数组中,选取两个数,使其异或值\(>=K\),并且要让选择这两个数的间距尽可能小。
在字典树上每个结点维护其包含的数的最大下标。
从前往后插入,先查询后插入,对于要插入的数字\(a[i]\),即可查出其某个最接近它的数的位置\(pos\),且保证\((a[pos] \bigoplus a[i])<=K\)(做法与第一次测试的E题一样,都是分类讨论,这里不做讲解。可以参考第一次测试的pdf)

每一组询问得到的合法区间为\([pos+1,i]\),将其按要求排序即可。

Code:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<map>
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int tr[N][2];
typedef long long ll;
int tot;
int rt;
int n,k;
#define debug(x) cout<<#x<<" :"<<x<<endl
int rpos[N];
inline int read() {
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0',c=getchar();}
    return x*f;
}
void insert(ll val,int id){
	int pos=rt;
	for(ll i=31;i>=0;--i){
		int v = (1&(val>>(i*1ll)));
		if(!tr[pos][v])tr[pos][v]=++tot;
		rpos[pos] = max(rpos[pos],id);
		pos = tr[pos][v];

	}
	rpos[pos] = max(rpos[pos],id);
}
int query(ll x,ll v){
	int pos=rt;
	int ans=0;
	int maxpos=-1;
	for(ll i=31;i>=0;--i){
		int xp = (1&(x>>(i*1ll)));
		int vp = (1&(v>>(i*1ll)));
		if(pos == 0){
			return maxpos;
		}
		if(xp==0&&vp==0){
			if(tr[pos][1]){
				maxpos=max(maxpos,rpos[tr[pos][1]]);
				pos=tr[pos][0];
			}
			else{
				if(tr[pos][0]){
					pos=tr[pos][0];
				}
				else return maxpos;
			}

		}
		else if(xp==0&&vp==1){
			if(tr[pos][1]){
				pos=tr[pos][1];
			}
			else{
				return maxpos;
			}
		}
		else if(xp==1&&vp==0){
			if(tr[pos][0]){
				maxpos=max(maxpos,rpos[tr[pos][0]]);
				pos=tr[pos][1];
			}
			else{
				if(tr[pos][1]){
					pos=tr[pos][1];
				}
				else return maxpos;
			}
		}
		else if(xp==1&&vp==1){
			if(tr[pos][0]){
				pos=tr[pos][0];
			}
			else{
				return maxpos;
			}
		}

	}
	maxpos=max(rpos[pos],maxpos);
	return maxpos;
}
void init(){
	tot=rt=1;
}
struct node{
	int l,r;
	node(int _l,int _r):l(_l),r(_r){}
};
bool cmp(node a,node b){
	if(a.r-a.l == b.r-b.l){
		return a.l<b.l;
	}
	else return a.r-a.l < b.r-b.l;
}

int q[500005];
int main(){
	int cas=0;
	int t;
	scanf("%d",&t);
	for(int i=1;i<=N-1;++i)rpos[i]=-1;
	while(t--){
		n=read(),k=read();
		for(int i=0;i<=tot;++i){
			for(int j=0;j<=1;++j){
				tr[i][j]=0;
			}
			rpos[i]=-1;
		}
		init();

		for(int i=1;i<=n;++i){
			q[i]=read();
		}
		for(int i=1;i<=n;++i){
			q[i]=(q[i-1]^q[i]);
		}
		int ansl,ansr;
		int pos;
		int mlen = n+10;
		ansl=ansr=n+10;
		insert(0,0);
		vector<node>vans;
		for(int i=1;i<=n;++i){
			int pos = query(q[i],k);
			insert(q[i],i);
			if(pos == -1 || pos == i)continue;
			int len = i-pos;
			ansl=pos+1;
			ansr=i;
			vans.push_back(node(pos+1,i));

		}
		sort(vans.begin(),vans.end(),cmp);
		if((int)vans.size()==0)printf("-1\n");
		else printf("%d %d\n",vans[0].l,vans[0].r);
	}
	return 0;
}

D CFGym 101667I

题意:

咕咕咕

Sol:

考虑到答案要求\(k+p\)尽可能小,相同的同时需要\(p\)尽可能小。
将序列翻转,求出\(Next\)数组,于是可以枚举翻转后的前缀,相当于从大到小枚举\(k\).
对于每一个枚举的前缀,求其最小循环节\(p=i-Next[i]\),相当于对于枚举的每个\(k\),同时保证了\(p\)尽可能小。
对此,求出的结果一定包含在最优解中。枚举过程中找到最小的\(k+p\)值,将其对应的\(k\)和\(p\)输出即可。

最稳妥的做法就是找到最小值后再扫一遍,记录下\(k+p\)等于最小值的情况下\(p\)最小的值即可。

Code:

#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int t[N],p[N];
int next1[N];
void get_next(int p[],int lenp){
	int j=0;
	for(int i=2;i<=lenp;++i){
		while(j&&p[j+1]!=p[i])j=next1[j];
		if(p[j+1]==p[i])j++;
		next1[i]=j;
	}
}
void kmp(int t[],int lent,int p[],int lenp){
	int j=0;
	for(int i=1;i<=lent;++i){
		while(j&&p[j+1]!=t[i])j=next1[j];
		if(p[j+1]==t[i])j++;
		if(j==lenp){
			cout<<i-lenp+1<<endl;//输出p在t中出现的位置
		}
	}
}
void print(int lenp){
	for(int i=1;i<=lenp;++i){
		cout<<next1[i]<<" ";
	}
}
int n;
int a[N];
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	for(int i=1;i<=n;++i){
		p[i]=a[n-i+1];
	}
	int lenp=n;
	get_next(p,lenp);//预处理p的next数组
	int minn=0x3f3f3f3f;
	int ansp,ansk;
	for(int i=1;i<=n;++i){
		int pp=(i-next1[i]);
		int kk=n-i;
		if(pp+kk<minn){
			minn=pp+kk;
			ansp=pp,ansk=kk;
		}
	}
	cout<<ansk<<" "<<ansp;
	return 0;
}

E CFGym 100502H

题意:

有两个时钟上面有\(n\)个指针,给出的数字代表指针的角度。问能否在某一时刻使得两个时钟的指针重合。

Sol:

参考了这份大佬的题解
容易想到先对指针角度排序,然后相邻指针相减得到一个间距。如果这些间距能够相同的话,那么就代表可以在某个时刻重合。

最暴力地做法就是\(O(n^2)\)的复杂度。用第一个时钟的每一个间距去匹配第二个时钟的每一个间距,如果发现有能够匹配到的就说明可以。

因为是环状的,所以要让第一个时钟的间距向后延伸\(n-1\)个。

然后让第一个时钟的间距当文本串,第二个时钟的间距当模式串,然后跑一下\(KMP\)就好了。

Code:

#include <bits/stdc++.h>
using namespace std;
#define N 200010
const int maxdeg = 360000;
int nxt[N], a[N], b[N], c[N+N], d[N], n;

void make_next() {
    for(int i = 0; i <= n; i++) nxt[i] = 1;
    int k = 0, j = 1;
    nxt[1] = 0;
    while(j <= n) {
        if(k == 0 || d[k] == d[j]) {
            k++; j++;
            nxt[j] = k;
        } else k = nxt[k];
    }
}

bool KMP() {
    int k = 1, j = 1;
    make_next();
    while(j < 2 * n) {
        if(k == 0 || d[k] == c[j]) {
            k++; j++;
            if(k == n) return true;
        } else k = nxt[k];
    }
    return false;
}

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + n);
    for(int i = 2; i <= n; i++) {
        c[i] = a[i] - a[i-1];
        d[i] = b[i] - b[i-1];
    }
    c[1] = a[1] - a[n] + maxdeg;
    d[1] = b[1] - b[n] + maxdeg;
    for(int i = n + 1; i < 2 * n; i++)
        c[i] = c[i-n];
    if(KMP()) puts("possible");
    else puts("impossible");
    return 0;
}

F HDU 4821

题意:

给出\(M\)和\(L\),和一个字符串\(S\)。要求找出\(S\)的子串中长度为\(L*M\),并且可以分成\(M\)段,每段长\(L\),并且\(M\)段都不相同的子串个数。

Sol:

考察复杂度计算和想法的一道题。
枚举段长\(L\)的起始点位置,即枚举\([1,L]\),每次枚举存储下所有段长为\(L\)的不重叠子串\(Hash\)值。
为什么只需要枚举\([1,L]\)。举个例子
对于样例\(abcabcbcaabc,M=3,L=3\),
枚举起点\(i=1\)时,可以得到段长为3的所有子串是\(abc,abc,bca,abc\)。
枚举起点\(i=4\)时,可以得到段长为3的所有子串是\(abc,bca,abc\)。
可见后者是前者的子集,对此只需枚举\([1,L]\)即可。

每次枚举存储下所有段长为L的子串\(Hash\)值后,就可以进行操作了.
类似滑动窗口的维护。
先维护变量\(cnt\)表示前\(m\)个长度为\(L\)的子串中不同子串的个数,每次移动时只需要删除窗口的第一个字符串的\(Hash\)值,再将当前窗口的下一个字符串的\(Hash\)值加入窗口,一直维护下去即可。

时间复杂度:\(O(L*(Len/L))=O(Len)\)。

Code:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define debug(x) cout<<#x<<" :"<<x<<endl
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N=1e5+20;
inline ll read() {
    char c=getchar();ll x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0',c=getchar();}
    return x*f;
}
inline void out(int x) {
    if(x>9) out(x/10);
    putchar(x%10+'0');
}

ll jc[N];
ll mod=386910137;
ll bas=13331;
ll ha[N];
ll getlr(int l,int r){
    return ((ha[r]-ha[l-1]*jc[r-l+1])%mod+mod)%mod;
}

int n,m,k;
char s[N];
int l;
void init(){
    jc[0]=1;
    for(int i=1;i<=N-1;++i){
        jc[i]=(jc[i-1]*bas)%mod;
    }
}
ll kep[N];
void solve(){
    scanf("%s",s+1);
    int lens=strlen(s+1);
    for(int i=1;i<=lens;++i){
        ha[i]=(ha[i-1]*bas+s[i]-'a'+1)%mod;
    }
    int ans=0;
    rep(i,1,l){
        int con=0;
        int st=i;
        while(st+l-1<=lens){
            kep[++con] = getlr(st,st+l-1);
            st+=l;
        }
        unordered_map<ll,int>mp;
        int cnt=0;
        rep(j,1,n){
            if(!mp[kep[j]])cnt++;
            mp[kep[j]]++;
        }
        if(cnt==n)ans++;
        rep(j,n+1,con){
            mp[kep[j-n]]--;
            if(mp[kep[j-n]]==0)cnt--;
            mp[kep[j]]++;
            if(mp[kep[j]]==1)cnt++;
            if(cnt==n)ans++;
        }
    }
    printf("%d\n",ans);
}

int main(){
    int tt;
    tt=1;
    init();
    while(scanf("%d%d",&n,&l)!=EOF){
        solve();
    }
    return 0;
}