题目描述:
一个正整数叫作 bobo number 当且仅当它在十进制下可以表示成两个相同的整数(无前导0)拼接而成的结果。例如:12341234和3232都是 bobo number ,但1234321和123401234不是。
一个正整数叫作 almost bobo number 当且仅当这个整数在合并连续相同的数位后,其结果为 bobo number 。例如:1112223112233合并连续相同的数位后变成123123,所以它是 almost bobo number 。
Bobo 有一个非常大的正整数 n ,他想知道,严格小于 n 的最大的 almost bobo number 是多少。
n的位数不超过5000000.
解题思路:
本题解法:KMP(难)
显然,如果 n≤1010 ,无解。否则可以找到一组解。
如果 s 有 d(d>4) 位,那么 999…999898 ,即 (d−4) 个9后面加898,共 (d−1) 位也是bobo。如果不存在跟 s 同样长度的almost bobo number,那么上面的这组解即为最优解。现在,我们可以假设存在跟 s 同样长度的almost bobo number 。
显然,最优解一定跟 s 有最长的前缀,我们把这个前缀定义为 p 。为了找到 p ,我们枚举所有的前缀,然后判断每个前缀是否可能有解。
假设前缀为 s′ ,我们需要加一个数字 c(c < < <script type="math/tex" id="MathJax-Element-206"><</script>s[|s′|+1]) 。我们把 s′c 定义为字符串 t ,并将 t 合并相同数位。为了添加其它数字使得 t 变成bobo number ,我们需要找一个 t 的一个border u(即 t=uxu ,x为非空字符串),那么 t 后面只需要加 x 就可以变成 bobo 了。我们只需要保证 |t|−2|u|≤|s|−|s′|−1 即可保证有解(有些情况可能需要特判)。所以,我们需要找到字符串 t 的最长的长度小等于|t|/2的border 。用KMP可以O(1)求出。
找到最长前缀之后,我们只需要枚举每一位,从9到0,找到一个最大的答案(还是可以用上述的判断方法检查选中的前缀是否有解)。
注意本题要动态维护fail链,如果每次暴力跳就会是 O(n2) O ( n 2 ) 的,可以参考代码中的方法做到 O(n) O ( n ) ,还有 O(1) O ( 1 ) 求合法border的方法也可以参考代码。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=5000005;
int n,ans[N],t[N],nxt[N],fail[N][10];
char s[N];
void ext(int c,int len)
{
nxt[len]=fail[len-1][c];t[len]=c;
fail[len-1][c]=len;
memcpy(fail[len],fail[nxt[len]],sizeof(fail[len]));
}
int find(int i,int len)
{
if(nxt[i]*2<=len)return nxt[i];
int d=i-nxt[i],c=(i-len/2-1)/d;
return nxt[i-c*d];
}
bool check(int c,int i,int len)
{
if(t[len-1]==c)
{
if(len==2)return n-i>=3;
int b=find(len-1,len-1);
return len-1-2*b<=n-i;
}
int tmp=fail[len-1][c];
ext(c,len);int b=find(len,len);
fail[len-1][c]=tmp;
return len-2*b<=n-i;
}
bool solve()
{
n=strlen(s+1);t[0]=10;
if(n<4)return false;
int i,j,mx=0;
memset(fail[0],0,sizeof(fail[0]));
for(i=1,j=1;i<=n;i++)
{
for(int c=s[i]-'0'-1;c>=(i==1);c--)
if(check(c,i,j)){mx=i;break;}
if(s[i]!=s[i-1])ext(s[i]-'0',j++);
}
if(!mx)
{
if(n<=4)return false;
n--,ans[n]=ans[n-2]=8,ans[n-1]=9;
for(i=1;i<=n-3;i++)ans[i]=9;
return true;
}
memset(fail[0],0,sizeof(fail[0]));
for(i=j=1;i<mx;ans[i]=s[i]-'0',i++)
if(s[i]!=s[i-1])ext(s[i]-'0',j++);
for(i=mx;i<=n;i++)
{
for(int c=i==mx?s[i]-'0'-1:9;c>=(i==1);c--)
if(check(c,i,j)){ans[i]=c;break;}
if(j==1||ans[i]!=ans[i-1])ext(ans[i],j++);
}
return true;
}
int main()
{
//freopen("lx.in","r",stdin);
//freopen("lx.out","w",stdout);
while(scanf("%s",s+1)!=EOF)
if(solve())
{
for(int i=1;i<=n;i++)putchar('0'+ans[i]);
putchar('\n');
}
else puts("-1");
return 0;
}