1.字符串专题

字符串专题

\(A\) CF1037H Security

\(B\) CF1073G Yet Another LCP Problem

  • 多倍经验: luogu P7409 SvT

  • \(a\)\(b\) 拼起来得到 \(c\) ,由容斥有 \(\sum\limits_{i=1}^{k}\sum\limits_{j=1}^{l}LCP(s_{a_{i} \sim n},s_{b_{j} \sim n})=\frac{\sum\limits_{i=1}^{k+l}\sum\limits_{j=1}^{k+l}LCP(s_{c_{i} \sim n},s_{c_{j} \sim n})-\sum\limits_{i=1}^{k}\sum\limits_{j=1}^{k}LCP(s_{a_{i} \sim n},s_{a_{j} \sim n})-\sum\limits_{i=1}^{l}\sum\limits_{j=1}^{l}LCP(s_{b_{i} \sim n},s_{b_{j} \sim n})}{2}\)

  • \(a\) 为例,先将 \(a\) 按照 \(rk\) 排序,由 \(sa_{rk_{a_{i}}}=a_{i}\) 可以构造 \(\begin{aligned} g_{i}=LCP(a_{i-1},a_{i})=\begin{cases} n-a_{i}+1 &a_{i-1}=a_{i} \\ \min\limits_{j=rk_{a_{i-1}+1}}^{rk_{a_{i}}} \{ height_{j} \} &a_{i-1} \ne a_{i} \end{cases} \end{aligned}\) ,特别的,有 \(g_{1}=0\) 。此时有 \(\begin{aligned} \sum\limits_{i=1}^{k}\sum\limits_{j=1}^{k}\frac{LCP(s_{a_{i} \sim n},s_{a_{j} \sim n})}{2} &=\sum\limits_{i=1}^{k}\sum\limits_{j=i+1}^{k}LCP(s_{a_{i} \sim n},s_{a_{j} \sim n})+\sum\limits_{i=1}^{k}\frac{LCP(s_{a_{i} \sim n},s_{a_{i} \sim n})}{2} \\ &=\sum\limits_{i=1}^{k}\sum\limits_{j=i+1}^{k}\min\limits_{h=rk_{a_{i}}+1}^{rk_{a_{j}}} \{ height_{h} \}+\sum\limits_{i=1}^{k}\frac{n-a_{i}+1}{2} \\ &=\sum\limits_{i=1}^{k}\sum\limits_{j=i+1}^{k}\min\limits_{h=i+1}^{j} \{ g_{h} \}+\sum\limits_{i=1}^{k}\frac{n-a_{i}+1}{2} \\ &=\sum\limits_{i=2}^{k}\sum\limits_{j=i}^{k}\min\limits_{h=i}^{j} \{ g_{h} \}+\sum\limits_{i=1}^{k}\frac{n-a_{i}+1}{2} \\ &=\sum\limits_{i=1}^{k}\sum\limits_{j=i}^{k}\min\limits_{h=i}^{j} \{ g_{h} \}-\sum\limits_{i=1}^{k}0+\sum\limits_{i=1}^{k}\frac{n-a_{i}+1}{2} \\ &=\sum\limits_{i=1}^{k}\sum\limits_{j=i}^{k}\min\limits_{h=i}^{j} \{ g_{h} \}+\sum\limits_{i=1}^{k}\frac{n-a_{i}+1}{2} \end{aligned}\)

  • \(g_{i}\) 可以使用 \(ST\) 表预处理,然后类似 luogu P6503 [COCI2010-2011#3] DIFERENCIJA 使用单调栈维护即可。

    点击查看代码
    ll sa[400010],rk[800010],oldrk[800010],id[400010],cnt[400010],key[400010],height[400010],a[400010],b[400010],c[400010],fminn[400010][20],f[400010],g[400010];
    char s[400010];
    bool cmp(ll a,ll b)
    {
    	return rk[a]<rk[b];
    }
    ll val(char x)
    {
    	return (ll)x;
    }
    void counting_sort(ll n,ll m)
    {
    	memset(cnt,0,sizeof(cnt));
    	for(ll i=1;i<=n;i++)
    	{
    		cnt[key[i]]++;
    	}
    	for(ll i=1;i<=m;i++)
    	{
    		cnt[i]+=cnt[i-1];
    	}
    	for(ll i=n;i>=1;i--)
    	{
    		sa[cnt[key[i]]]=id[i];
    		cnt[key[i]]--;
    	}
    }
    void init_sa(char s[],ll len)
    {
    	ll m=127,tot=0,num=0,i,j,w;
    	for(i=1;i<=len;i++)
    	{
    		rk[i]=val(s[i]);
    		id[i]=i;
    		key[i]=rk[id[i]];
    	}
    	counting_sort(len,m);
    	for(w=1;tot!=len;w<<=1,m=tot)
    	{
    		num=0;
    		for(i=len;i>=len-w+1;i--)
    		{
    			num++;
    			id[num]=i;
    		}
    		for(i=1;i<=len;i++)
    		{
    			if(sa[i]>w)
    			{
    				num++;
    				id[num]=sa[i]-w;
    			}
    		}
    		for(i=1;i<=len;i++)
    		{
    			key[i]=rk[id[i]];
    		}
    		counting_sort(len,m);
    		for(i=1;i<=len;i++)
    		{
    			oldrk[i]=rk[i];
    		}
    		tot=0;
    		for(i=1;i<=len;i++)
    		{
    			tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]);
    			rk[sa[i]]=tot;
    		}
    	}
    	for(i=1,j=0;i<=len;i++)
    	{
    		j-=(j>=1);
    		while(s[i+j]==s[sa[rk[i]-1]+j])
    		{
    			j++;
    		}
    		height[rk[i]]=j;
    	}
    }
    void init(ll n,ll a[])
    {
    	memset(fminn,0x3f,sizeof(fminn));
    	for(ll i=1;i<=n;i++)
    	{
    		fminn[i][0]=a[i];
    	}
    	for(ll j=1;j<=log2(n);j++)
    	{
    		for(ll i=1;i<=n-(1<<j)+1;i++)
    		{
    			fminn[i][j]=min(fminn[i][j-1],fminn[i+(1<<(j-1))][j-1]);
    		}
    	}
    }
    ll query(ll l,ll r)
    {
    	ll t=log2(r-l+1);
    	return min(fminn[l][t],fminn[r-(1<<t)+1][t]);
    }
    ll ask(ll n,ll k,ll a[])
    {
    	ll sum=0,i;
    	stack<ll>st;
    	sort(a+1,a+1+k,cmp);
    	for(i=1;i<=k;i++)
    	{
    		g[i]=((a[i]==a[i-1])?n-a[i]+1:query(rk[a[i-1]]+1,rk[a[i]]));   
    		sum+=(n-a[i]+1)/2;//其实这里这样写是不太严谨的,但这一行的可以互相消去的,所以就这样写了
    	}
    	for(i=1;i<=k;i++)
    	{
    		while(st.empty()==0&&g[st.top()]>=g[i])
    		{
    			st.pop();
    		}
    		if(st.empty()==0)
    		{
    			f[i]=f[st.top()]+g[i]*(i-st.top());
    		}
    		else
    		{
    			f[i]=f[0]+g[i]*(i-0);
    		}
    		st.push(i);
    		sum+=f[i];
    	}
         return sum;
    }
    int main()
    {
    	ll n,m,k,l,i,j;
    	cin>>n>>m>>(s+1);
    	init_sa(s,n);
    	init(n,height);
    	for(j=1;j<=m;j++)
    	{
    		cin>>k>>l;
    		for(i=1;i<=k;i++)
    		{
    			cin>>a[i];
    			c[i]=a[i];
    		}
    		for(i=1;i<=l;i++)
    		{
    			cin>>b[i];
    			c[i+k]=b[i];
    		}
    		cout<<ask(n,k+l,c)-ask(n,k,a)-ask(n,l,b)<<endl;
    	}
    	return 0;
    }
    

\(C\) CF906E Reverses

\(D\) CF666E Forensic Examination

\(E\) luogu P4199 万径人踪灭

  • 因为需要 \(FFT\) ,所以被 \(miaomiao\) 毙了。

\(F\) CF1535F String Distance

  • 考虑 \(f(s_{i},s_{j})\) 的取值,题目中已保证 \(|s_{i}|=|s_{j}|\)

    • \(s_{i}\)\(s_{j}\) 字符集不同时,无法使 \(s_{i},s_{j}\) 相等,故 \(f(s_{i},s_{j})=1337\)
    • \(s_{i}\)\(s_{j}\) 字符集相同,且 \(s_{i}=s_{j}\) 时,有 \(f(s_{i},s_{j})=0\)
      • 因保证字符串两两不相同,可以不考虑这种情况。
    • \(s_{i}\)\(s_{j}\) 字符集相同,且 \(s_{i} \ne s_{j}\) ,存在一组 \(l,r(1 \le l<r \le |s_{i}|=|s_{j}|)\) 使得 \(\begin{cases} s_{i,1 \sim l-1}=s_{j,1 \sim l-1} \\ s_{i,l \sim r} \ne s_{j,l \sim r} \\ \exists k \in \{ i,j \},s_{k,l} \le s_{k,l+1} \le s_{k,l+2} \le \dots \le s_{k,r} \\ s_{i, r+1 \sim |s_{i}|}=s_{j,r+1 \sim |s_{j}|} \end{cases}\) 时,对 \(s_{i \bigoplus j \bigoplus k}\)\([l,r]\) 进行一次排序即可,故 \(f(s_{i},s_{j})=1\)
    • \(s_{i}\)\(s_{j}\) 字符集相同,且 \(s_{i} \ne s_{j}\) ,不存在一组 \(l,r(1 \le l<r \le |s_{i}|=|s_{j}|)\) 使得 \(\begin{cases} s_{i,1 \sim l-1}=s_{j,1 \sim l-1} \\ s_{i,l \sim r} \ne s_{j,l \sim r} \\ \exists k \in \{ i,j \},s_{k,l} \le s_{k,l+1} \le s_{k,l+2} \le \dots \le s_{k,r} \\ s_{i, r+1 \sim |s_{i}|}=s_{j,r+1 \sim |s_{j}|} \end{cases}\) 时,分别对 \(s_{i},s_{j}\) 整体排序即可,故 \(f(s_{i},s_{j})=2\)
  • \(f(s_{i},s_{j})=1337\) 的情况容易判断,而 \(f(s_{i},s_{j})=2\) 可以通过计算 \(f(s_{i},s_{j})=1\) 的情况相减得到。现在考虑计算 \(f(s_{i},s_{j})=1\) 的情况。

  • 对于同一字符集的所有字符串先按字典序排序,设其分别为 \(s_{a_{1}} \sim s_{|a|}\) 。对于一段前缀 \(1 \sim l-1\) 相同的若干子串, \(l \sim r\) 有序的一定排在前面。

  • 考虑固定 \(a_{i}\) 的某个极长有序区间 \(l \sim r\) (左右均不能扩展),然后统计去掉这个区间后相等的 \(s_{a_{j}}(i<j \le |a|)\) 的个数。

  • 前缀相同的可以通过 \(LCP\) 处理。容易发现 \(LCP(s_{a_{i}},s_{a_{i+1}}) \dots LCP(s_{a_{i}},s_{a_{i+1}},s_{a_{i+2}}, \dots ,s_{|a|})\) 是单调不降的,且 \(LCP(s_{a_{i}},s_{a_{i+1}}, \dots ,s_{a_{j}})=\min\limits_{k=i+1}^{j} \{ LCP(s_{a_{k-1}},s_{a_{k}}) \}\) ,可以使用单调栈维护。

  • 后缀则处理每个字符串的极长有序区间,然后将反串插到一棵 \(Trie\) 树里面,并记录位置和编号,然后二分处理即可。

    • \(LCP\) 可以暴力求,也可以拼起来用 \(SA\)\(ST\) 表维护。
  • 假设当前定义了 vector<temlate>example ,则 example.clear() 的真正作用是将 example.size() 变成 \(0\) ,而不是清空内部元素,需要使用 vector<temlate>().swap(example)

    点击查看代码
    ll trie[200010][30],lcp[200010],tot=0,s_top=0;
    pair<ll,ll>st[200010];
    vector<ll>id[200010],duan[200010];
    string s,t;
    map<ll,ll>pos[200010];
    map<string,vector<string> >f;
    map<string,vector<string> >::iterator it;
    ll val(char x)
    {
    	return x-'a'+1;
    }
    void insert(string s,ll idx,ll len)
    {
    	ll x=0,i;
    	pos[idx][len]=x;
    	id[x].push_back(idx);
    	for(i=len-1;i>=0;i--)
    	{
    		if(trie[x][val(s[i])]==0)
    		{
    			tot++;
    			trie[x][val(s[i])]=tot;
    		}
    		x=trie[x][val(s[i])];
    		pos[idx][i]=x;
    		id[x].push_back(idx);
    	}
    }
    ll ask(ll x,ll l,ll r)
    {
    	return lower_bound(id[x].begin(),id[x].end(),r)-lower_bound(id[x].begin(),id[x].end(),l);
    }
    ll work(vector<string> a,ll len)
    {
    	ll ans=2*a.size()*(a.size()-1)/2,i,j;
    	for(i=0;i<=tot;i++)
    	{
    		for(j='a';j<='z';j++)
    		{
    			trie[i][val(j)]=0;
    		}
    		vector<ll>().swap(id[i]);
    	}
    	tot=0;
    	sort(a.begin(),a.end());
    	for(i=0;i<a.size();i++)
    	{
    		pos[i].clear();
    		insert(a[i],i,len);
    	}
    	for(i=1;i<a.size();i++)
    	{
    		for(lcp[i]=0;lcp[i]<len-1;lcp[i]++)
    		{
    			if(a[i][lcp[i]]!=a[i-1][lcp[i]])
    			{
    				break;
    			}
    		}
    	}
    	for(i=0;i<a.size();i++)
    	{
    		vector<ll>().swap(duan[i]);
    		for(j=1;j<len;j++)
    		{
    			if(a[i][j-1]>a[i][j])
    			{
    				duan[i].push_back(j);
    			}
    		}
    		duan[i].push_back(len);
    	}
    	s_top=1;
    	st[s_top]=make_pair(a.size(),-1);
    	for(i=a.size()-1;i>=0;i--)
    	{
    		for(j=2;j<=s_top;j++)
    		{
    			ans-=1*ask(pos[i][*upper_bound(duan[i].begin(),duan[i].end(),st[j].second)],st[j].first,st[j-1].first);
    		}
    		while(s_top>=1&&st[s_top].second>=lcp[i])
    		{
    			s_top--;
    		}
    		s_top++;
    		st[s_top]=make_pair(i,lcp[i]);
    	}
    	return ans;
    }
    int main()
    {
    	ll n,len,ans=0,sum=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>s;
    		t=s;
    		len=s.size();
    		sort(t.begin(),t.end());
    		f[t].push_back(s);
    	}
    	for(it=f.begin();it!=f.end();it++)
    	{
    		ans+=1337*(it->second.size())*sum+work(it->second,len);
    		sum+=it->second.size();
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

\(G\) CF1400F x-prime Substrings

  • 打表得到 \(x=1 \sim 20\)\(x-prime\) 数量,观察到其数量较少。考虑对得到的 \(x-prime\) 建立 \(AC\) 自动机。

    点击查看数量
    sum[1]=1
    sum[2]=1
    sum[3]=1
    sum[4]=1
    sum[5]=3
    sum[6]=1
    sum[7]=8
    sum[8]=3
    sum[9]=8
    sum[10]=7
    sum[11]=54
    sum[12]=2
    sum[13]=139
    sum[14]=23
    sum[15]=40
    sum[16]=15
    sum[17]=928
    sum[18]=6
    sum[19]=2399
    sum[20]=29
    
  • \(f_{i,j}\) 表示前 \(i\) 个字符,当前运行到 \(AC\) 自动机的状态 \(j\) ,且没有 \(x-prime\) 时最少需要删除的字符个数,状态转移方程为 \(f_{i,j}=\min(f_{i-1,j}+1,\min\limits_{(k,j) \in E} \{ f_{i-1,k} \})\) ,其中 \(j\) 不是 \(Trie\) 树上的叶子节点。

  • 最终,有 \(\min\limits \{ f_{|s|,i} \}\) 即为所求。

    点击查看代码
    int t[30],trie[50000][15],fail[50000],vis[50000],f[2][50000],tot=0;
    char s[1010];
    int val(char x)
    {
    	return x-'0';
    }
    void insert(int s[],int len)
    {
    	int x=0,i;
    	for(i=1;i<=len;i++)
    	{
    		if(trie[x][s[i]]==0)
    		{
    			tot++;
    			trie[x][s[i]]=tot;
    		}
    		x=trie[x][s[i]];
    	}
    	vis[x]=1;
    }
    void build()
    {
    	int x,i;
    	queue<int>q;
    	for(i=1;i<=9;i++)
    	{
    		if(trie[0][i]!=0)
    		{
    			fail[trie[0][i]]=0;
    			q.push(trie[0][i]);
    		}
    	}
    	while(q.empty()==0)
    	{
    		x=q.front();
    		q.pop();
    		for(i=1;i<=9;i++)
    		{
    			if(trie[x][i]==0)
    			{
    				trie[x][i]=trie[fail[x]][i];
    			}
    			else
    			{
    				fail[trie[x][i]]=trie[fail[x]][i];
    				q.push(trie[x][i]);
    			}
    		}
    	}
    }
    bool check(int len,int x)
    {
    	int sum,l,r;
    	for(l=1;l<=len;l++)
    	{
    		sum=0;
    		for(r=l;r<=len;r++)
    		{
    			sum+=t[r];
    			if(sum!=x&&x%sum==0)
    			{
    				return false;
    			}
    		}
    	}
    	return true;
    }
    void dfs(int len,int sum,int x)
    {
    	if(sum==0)
    	{
    		if(check(len,x)==true)
    		{
    			insert(t,len);
    		}	
    	}
    	else
    	{
    		for(int i=1;i<=min(sum,9);i++)
    		{
    			t[len+1]=i;
    			dfs(len+1,sum-i,x);
    		}
    	}
    }
    int main()
    {
    	int x,n,ans=0x7f7f7f7f,i,j;
    	cin>>(s+1)>>x;
    	n=strlen(s+1);
    	dfs(0,x,x);
    	build();
    	for(i=1;i<=n;i++)
    	{
    		memset(f[i&1],0x3f,sizeof(f[i&1]));
    		for(j=0;j<=tot;j++)
    		{
    			f[i&1][j]=min(f[i&1][j],f[(i-1)&1][j]+1);
    			if(vis[trie[j][val(s[i])]]==0)
    			{
    				f[i&1][trie[j][val(s[i])]]=min(f[i&1][trie[j][val(s[i])]],f[(i-1)&1][j]);
    			}
    		}
    	}
    	for(i=0;i<=tot;i++)
    	{
    		ans=min(ans,f[n&1][i]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

\(H\) CF955D Scissors

  • 哈希或 \(KMP\) 预处理出 \(t_{1 \sim i}\)\(s\) 中出现的最早位置 \(l_{i}\) 使得 \(t_{1 \sim i}=s_{l_{i}-i+1 \sim l_{i}}\)\(t_{m-i+1 \sim m}\)\(s\) 中出现的最晚位置 \(r_{m-i+1}\) 使得 \(t_{m-i+1 \sim m}=s_{r_{m-i+1} \sim r_{m-i+1}+i-1}\)

  • 对拼不起来的情况进行大力分讨。

    点击查看代码
    const ll mod=1000000007,base=13331;
    ll jc[500010],a[500010],b[500010],l[500010],r[500010];
    char s[500010],t[500010];
    void sx_hash(char s[],ll a[],ll len)
    {
    	for(ll i=0;i<=len;i++)
    	{
    		a[i]=(i==0)?0:(a[i-1]*base%mod+s[i])%mod;
    	}
    }
    ll ask_hash(ll a[],ll l,ll r)
    {
    	return (a[r]-a[l-1]*jc[r-l+1]%mod+mod)%mod;
    }
    int main()
    {
    	ll n,m,k,flag=0,i,j;
    	cin>>n>>m>>k>>(s+1)>>(t+1);
    	for(i=0;i<=n;i++)
    	{
    		jc[i]=(i==0)?1:jc[i-1]*base%mod;
    	}
    	sx_hash(s,a,n);
    	sx_hash(t,b,m);
    	j=k;
    	for(i=1;i<=min(m,k);i++)
    	{
    		while(j<=n&&ask_hash(b,1,i)!=ask_hash(a,j-i+1,j))
    		{
    			j++;
    		}
    		if(ask_hash(b,1,i)==ask_hash(a,k-i+1,k))
    		{
    			j=k;
    		}
    		l[i]=j;
    	}
    	j=n-k+1;
    	for(i=1;i<=min(m,k);i++)
    	{
    		while(j>=1&&ask_hash(b,m-i+1,m)!=ask_hash(a,j,j+i-1))
    		{
    			j--;
    		}
    		if(ask_hash(b,m-i+1,m)==ask_hash(a,n-k+1,n-k+i))
    		{
    			j=n-k+1;
    		}
    		r[m-i+1]=j;
    	}
    	for(i=1;i<=m;i++)
    	{
    		if(l[i]<r[i+1]&&1<=l[i]-k+1&&l[i]<=n&&1<=r[i+1]&&r[i+1]+k-1<=n)
    		{
    			flag=1;
    			cout<<"Yes"<<endl;
    			cout<<l[i]-k+1<<" "<<r[i+1]<<endl;
    			break;
    		}
    	}
    	if(flag==0)//如果拼不起来
    	{
    		for(i=1;i+m-1<=n;i++)
    		{
    			if(ask_hash(b,1,m)==ask_hash(a,i,i+m-1))
    			{
    				flag=1;
    				cout<<"Yes"<<endl;
    				if(i+m-1>=k)
    				{
    					if(i+m+k-1<=n)//强制分给左面
    					{
    						cout<<i+m-1-k+1<<" "<<i+m<<endl;								
    					}
    					else//强制分给右面
    					{
    						if(i<=n-k+1)
    						{
    							cout<<1<<" "<<i<<endl;
    						}
    						else
    						{
    							cout<<1<<" "<<n-k+1<<endl;
    						}
    					}
    				}
    				else//强制分给左面
    				{
    					cout<<1<<" "<<k+1<<endl;
    				}
    				break;
    			}
    		}
    		if(flag==0)
    		{
    			cout<<"No"<<endl;	
    		}
    	}
    	return 0;
    }
    

\(I\) CF1530E Minimax

  • What is 前缀函数

  • \(cnt_{i}\) 表示 \(i\)\(s\) 中出现的次数。

  • 观察到若尽量使 \(f(t)\) 尽可能小,要尽可能将其中一部分字符串用其他字符隔开。

  • 首先将 \(s\) 按字典序升序排序,然后大力分讨。

    • \(s_{1}=s_{|s|}\) 时,有 \(t=s\) 即为所求,此时 \(f(t)=\max\limits_{i=1}^{|t|} \{ nxt_{i} \}=nxt_{n}=|t|-1\)
    • \(s_{1} \ne s_{|s|},\exists i \in [1,|s|],cnt_{s_{i}}=1\) 时,先放一个 \(s_{i}\) ,然后按字典序输出即可。此时 \(f(t)=0\)
    • \(s_{1} \ne s_{|s|},0 \le cnt_{s_{1}}-2 \le |s|-cnt_{s_{1}},\forall i \in [1,|s|],cnt_{s_{i}}>1\) 时,考虑构造 \(f(t)=1\) 。又因要保证字典序最小,故 \(Border\) 一定由 \(s_{1}\) 产生。先放两个 \(s_{1}\) ,然后分别放置一个字典序次小的(若放完了,则放次次小的)和一个 \(s_{1}\) ,直到 \(s_{1}\) 放完,然后按字典序输出即可。
    • \(s_{1} \ne s_{|s|},cnt_{s_{1}}-2>n-cnt_{s_{1}},\forall i \in [1,|s|],cnt_{s_{i}}>1\) 且只包含两种字符时,考虑构造 \(f(t)=1\)\(Border\) 的来源同理。先放一个 \(s_{1}\) ,再把字典序次小的放完,再把 \(s_{1}\) 放完即可。
    • \(s_{1} \ne s_{|s|},cnt_{s_{1}}-2>n-cnt_{s_{1}},\forall i \in [1,|s|],cnt_{s_{i}}>1\) 且包含三种或三种以上字符时,考虑构造 \(f(t)=1\)\(Border\) 的来源同理。先放一个 \(s_{1}\) 和一个字典序最小的,再把 \(s_{1}\) 放完,再放一个字典序次次小的,然后按字典序输出即可。
    点击查看代码
    int cnt[30];
    char s[100010];
    int val(char x)
    {
    	return x-'a'+1;
    }
    int main()
    {
    	int t,n,id,id1,id2,i,j,k;
    	cin>>t;
    	for(k=1;k<=t;k++)
    	{
    		cin>>(s+1);
    		n=strlen(s+1);
    		id=id1=id2=0;
    		sort(s+1,s+1+n);
    		if(s[1]==s[n])
    		{
    			cout<<(s+1)<<endl;
    		}
    		else
    		{
    			memset(cnt,0,sizeof(cnt));    
    			for(i=1;i<=n;i++)
    			{
    				cnt[val(s[i])]++;
    			}
    			for(i='a';i<='z';i++)
    			{
    				if(cnt[val(i)]==1)
    				{
    					id=i;
    					break;
    				}
    			}
    			if(id!=0)
    			{
    				cout<<(char)id;
    				cnt[val(id)]--;
    				for(i='a';i<='z';i++)
    				{
    					for(j=1;j<=cnt[val(i)];j++)
    					{
    						cout<<(char)i;
    					}
    				}
    				cout<<endl;
    			}
    			else
    			{
    				if(n-cnt[val(s[1])]>=cnt[val(s[1])]-2)
    				{
    					cout<<s[1]<<s[1];
    					cnt[val(s[1])]-=2;
    					while(cnt[val(s[1])]>=1)
    					{
    						for(i=s[1]+1;i<='z';i++)
    						{
    							if(cnt[val(i)]>=1)
    							{
    								cout<<(char)i<<s[1];
    								cnt[val(i)]--;
    								cnt[val(s[1])]--;
    								break;
    							}
    						}
    					}
    					for(i=s[1]+1;i<='z';i++)
    					{
    						for(j=1;j<=cnt[val(i)];j++)
    						{
    							cout<<(char)i;
    						}
    					}
    					cout<<endl;
    				}
    				else
    				{
    					for(i=s[1]+1;i<='z';i++)
    					{
    						if(cnt[val(i)]>=1)
    						{
    							id1=i;
    							break;
    						}
    					}
    					if(cnt[val(s[1])]+cnt[val(id1)]==n)
    					{
    						cout<<s[1];
    						cnt[val(s[1])]--;
    						for(i=1;i<=cnt[val(id1)];i++)
    						{
    							cout<<(char)id1;
    						}
    						for(i=1;i<=cnt[val(s[1])];i++)
    						{
    							cout<<s[1];
    						}
    						cout<<endl;
    					}
    					else
    					{
    						for(i=id1+1;i<='z';i++)
    						{
    							if(cnt[val(i)]>=1)
    							{
    								id2=i;
    								break;  
    							}
    						}
    						cout<<s[1]<<(char)id1;
    						cnt[val(s[1])]--;
    						cnt[val(id1)]--;
    						for(i=1;i<=cnt[val(s[1])];i++)
    						{
    							cout<<s[1];
    						}
    						cout<<(char)id2;
    						cnt[val(id2)]--;
    						for(i=s[1]+1;i<='z';i++)
    						{
    							for(j=1;j<=cnt[val(i)];j++)
    							{
    								cout<<(char)i;
    							}
    						}
    						cout<<endl; 
    					}
    				}
    			}
    		}
    	}
    	return 0;
    }
    
posted @ 2024-04-18 17:27  hzoi_Shadow  阅读(88)  评论(2编辑  收藏  举报
扩大
缩小