codeforces539D. Sasha and One More Name

Reading books is one of Sasha’s passions. Once while he was reading one book, he became acquainted with an unusual character. The character told about himself like that: “Many are my names in many countries. Mithrandir among the Elves, Tharkûn to the Dwarves, Olórin I was in my youth in the West that is forgotten, in the South Incánus, in the North Gandalf; to the East I go not.”

And at that moment Sasha thought, how would that character be called in the East? In the East all names are palindromes. A string is a palindrome if it reads the same backward as forward. For example, such strings as “kazak”, “oo” and “r” are palindromes, but strings “abb” and “ij” are not.

Sasha believed that the hero would be named after one of the gods of the East. As long as there couldn’t be two equal names, so in the East people did the following: they wrote the original name as a string on a piece of paper, then cut the paper minimum number of times k, so they got k+1

pieces of paper with substrings of the initial string, and then unite those pieces together to get a new string. Pieces couldn’t be turned over, they could be shuffled.

In this way, it’s possible to achive a string abcdefg from the string f|de|abc|g using 3 cuts (by swapping papers with substrings f and abc). The string cbadefg can’t be received using the same cuts.

More formally, Sasha wants for the given palindrome s
find such minimum k, that you can cut this string into k+1 parts, and then unite them in such a way that the final string will be a palindrome and it won’t be equal to the initial string s. It there is no answer, then print “Impossible” (without quotes).

Input
The first line contains one string s(1≤|s|≤5000) — the initial name, which consists only of lowercase Latin letters. It is guaranteed that s is a palindrome.

Output
Print one integer k — the minimum number of cuts needed to get a new name, or “Impossible” (without quotes).

Examples
Input

nolon

Output

2

Input

otto

Output

1

Input

qqqq

Output

Impossible

Input

kinnikkinnik

Output

1

给出一个回文序列,最少分割几次,可以将由分割产生的片段,重新排序产生一个新的回文序列。
首先考虑在何时一定不能产生新的回文序列。如序列"aaaaaaa"和"aadaa" ,这样的序列,无论如何分割,都不会产生新的回文序列,其判断依据为 在序列中,单一字符出现的次数 >= 序列的长度-1。
除去以上情况,可以证明的,一定在分割两次后产生新的回文序列。
现在从序列的左端开始,找出第一个子序列pref,包含原序列的[1,pref.len],且要求在这个子序列中必须有单一字符出现次数 == pref.len-1。这样序列pref一定不会与序列pref的翻转相同,即 pref != reverse(pref)。记序列suff,包含原序列的[len-pref.len+1,len],剩余的部分记做mid。因为原序列为回文序列,所以 pref != suff 。故将这三部分重新组合一定可以产生新的回文序列。

pref + mid + suff != suff + mid + pre

之后,则需要检测是否存在分割更少的情况。由于数据量的大小,是可以计算分割一次所能产生的所有新序列,现只要判断这个新序列 是否回文序列 且 是否与原序列相同 即可。


#include <stdio.h>
#include <climits>
#include <cstring>
#include <time.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <utility>
#include <vector>
#include <string>

#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;

int len;
string s;
bool isPalindromes(const string& t);
bool isSame(const string& s,const string& t);
int main(){
    ios::sync_with_stdio(false);

    cin>>s;
    len=s.size();

    bool isAllSame=true;
    rep(i,1,len/2){
        if(s[i]!=s[i-1]){
            isAllSame=false;
            break;
        }
    }
    
    if(isAllSame)
        cout<<"Impossible"<<endl;
    else{
        bool one=false;
        rep(seg,0,len/2){
            //make new string
            string t;
            rep(i,seg+1,len)
                t+=s[i];
            for(int i=0;i<=seg;i++)
                t+=s[i];

            //judge
            if(!isSame(s,t) && isPalindromes(t)){
                cout<<"1"<<endl;
                one=true;
                break;
            }

        }

        if(!one)
            cout<<"2"<<endl;
    }
    re 0;
}
bool isSame(const string& s,const string& t){
    int len=s.size();
    rep(i,0,len)
        if(s[i]!=t[i])
            return false;
    return true;
}
bool isPalindromes(const string& t){
    int len=t.size();
    rep(i,0,len/2){
        if(t[i]!=t[len-i-1])
            return false;
    }
    return true;
}


在上述算法实现的过程中,不难发现,产生了大量重复计算,所以下一步,是否可以优化上述算法。
现在考虑所给出的回文序列的构成。对于一个回文序列,一定是一个镜像对称的序列,这个序列可能由重复的子序列构成,这样的子序列在这里称之为循环节,也有可能没有循环节。
如:序列 aadaa aadaa 具有循环节为 aadaa,而序列 acca 就不存在循环节。
根据之前所得出的结论,如果能产生新的回文序列,一定能在两次分割以内找到答案。那么,在什么情况下,只需要一次分割就能产生新的回文序列呢?首先考虑不存在循环节的序列,

  1. 当这个序列长度为奇数时
    根据回文序列的性质,一定有一个字符出现次数为奇数,且这个字符一定出现在这个序列对称轴的位置上,现记该字符为 x 。 对一个回文序列分割一次并重新组合,可以等效为下图所示的序列中取一个与所给序列长度(记为 len) 相同的子列,子列的起始位置为分割的位置。
    在这里插入图片描述 因为分割位置的不同,序列的对称轴位置会不断发生移动,如果对称轴位置上的字符不是 x ,因为字符x出现次数为奇数,那么一定不会得到一个对称的序列,也就是不可能在分割一次的情况下产生新的回文序列。
    当对称轴位置上的字符为x时,并假设这个序列为回文序列。根据已知,那么原序列和新序列对称轴位置上的字符一定为 x 。
    在这里插入图片描述
    且根据原序列与新序列的对称性,多次对称后,不难证出这个序列中只有 x 这一种字符,而这种情况是不可能出现的,因而即时对称轴位置上字符为 x ,也不可能仅由一次分割产生新的回文序列。
    综上所述,没有循环节且序列长度为奇数的回文序列,一定需要分割两次才能产生新的回文序列

  2. 序列长度为偶数时
    由于处于对称位置上的字符相同,而两对称的子序列不相同,所以交换左右对称的部分,对称位置上字符相同,但组成的新序列于原序列不同。所以没有循环节且序列长度为偶数时,只需要分割一次就能产生新的回文序列。

之后,再根据上述分析,考虑存在循环节的情况

  1. 循环节长度为奇数
    首先考虑循环节只重复两次的情况,之后可以通过递推的方法扩展到重复n次的情况。
    现在保留一个循环节,拆分另一个循环节,过程如下图所示
    在这里插入图片描述保留的部分原本就是对称的,但根据之前得出结论,这个长度为奇数的循环节不可能拆分为对称的部分。因此重复两次的情况一定需要分割两次。
    由此递推得,循环节为奇数时,一定需要分割两次

  2. 循环节长度为偶数时
    仍参照上述分割方法
    在这里插入图片描述取最左端循环节拆分,保留剩余部分(图中绿色部分)。因为剩余部分一定是一个回文序列,而拆分部分存在仅分割一次产生回文序列的方法,因此,循环节长度为偶数时,仅需分割一次。

综上所述,现在这个问题转化为找出循环单位的长度,奇数为2,偶数为1。
在处理这个问题时,可以提前预判,如果给出的序列长度为奇数,那么这个序列的最小循环单位长度一定为奇数。偶数时,可以不断将原序列折半,判断折半后子序列长度,并比较左右子序列是否相等。如果相等,那么子序列中一定存在循环节。


#include <stdio.h>
#include <climits>
#include <cstring>
#include <time.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <utility>
#include <vector>
#include <string>

#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define Make(a,b) make_pair(a,b)
#define Push(num) push_back(num)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;

int len;
string s;
void solve(const string& s);
int solveOdd(const string& s);
int solveEven(const string& s);
bool isPalindromes(const string& t);
bool isSame(const string& s,const string& t);
int main(){
    ios::sync_with_stdio(false);

    cin>>s;
    len=s.size();

    bool isAllSame=true;
    rep(i,1,len/2){
        if(s[i]!=s[i-1]){
            isAllSame=false;
            break;
        }
    }
    //cout<<"is all same? "<<(isAllSame? "true":"false")<<endl;
    if(isAllSame)
        cout<<"Impossible"<<endl;
    else{
        solve(s);
    }
    re 0;
}
void solve(const string& s){
    cout<<(s.size()%2 ? solveOdd(s):solveEven(s) )<<endl;
}
int solveOdd(const string& s){
    return 2;
}
int solveEven(const string& s){
    int len=s.size();
    if(len%2)
        return 2;
    else{
        string lf=s.substr(0,len/2);        //get substring of [left,right)
        string rg=s.substr(len/2,len/2);
        if(!isSame(lf,rg))
            return 1;
        else
            return solveEven(lf);
    }
}
bool isSame(const string& s,const string& t){
    int len=s.size();
    rep(i,0,len)
        if(s[i]!=t[i])
            return false;
    return true;
}
bool isPalindromes(const string& t){
    int len=t.size();
    rep(i,0,len/2){
        if(t[i]!=t[len-i-1])
            return false;
    }
    return true;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值