• 牛客小白月赛51 - 计算题(字符串哈希,二分)


    https://ac.nowcoder.com/acm/contest/11228/G

    题意
    你有一个长度为 n 的字符串,你可以选择删除一段后缀(可以为空),然后修改剩余串中的一个字符,使其变成回文串。
    请问最后能生成多少种可能的回文串?

    需要保证整个过程中的字符集仅包含小写字母。其中修改操作必须修改,但可将字符修改为原字符。

    思路
    修改一个元素之后,字符串的长度不变,所以对于 n 个删掉后缀后的字符串,只需要看这个字符串修改一个元素后能变成多少种,种类累加。

    对于一个字符串:

    • 如果已经是回文串了:如果长度为奇数,那么中间位置的元素可以随意修改,一共 26 种;如果长度为偶数,那么修改一个元素不能使其变成回文串,所以只有原串 1 种。
    • 如果左右只有一个位置不匹配:不管长度为奇数还是偶数,都可以使不匹配的左边位置变成右边位置或者右边位置变成左边位置,有 2 种方案。
    • 否则只修改一个元素不能使其变成回文串,有 0 种方案。

    如何判断一个串是否是回文串?
    将该串正着哈希一遍,然后倒过来哈希一遍,如果正反哈希值相同的话,那么这个串从前往后从后往前是相同的,便是回文的。

    如何判断一个串是否是仅有一个位置不匹配的回文串?
    从中间位置向外二分最大能满足的长度,找到第一个不满足的位置跳过,然后判断剩下的左右两串是否是回文的。如果是,说明只有那一个位置不满足。(或者从外向内二分最大能满足的串,找到第一个不满足的位置跳过,然后判断中间的串是否回文)

    Code

    #include
    using namespace std;
    
    #define Ios ios::sync_with_stdio(false),cin.tie(0)
    #define fi first
    #define se second
    #define endl '\n'
    
    const int N = 2000010, mod = 1e9+7;
    int T, n, m;
    char a[N];
    unsigned long long p[N], h1[N], h2[N];
    int P = 131;
    
    int queryPre(int l, int r)
    {
    	if(l > r) return 0;
    	return h1[r] - h1[l-1]*p[r-l+1];
    }
    int queryLast(int l, int r)
    {
    	if(l > r) return 0;
    	return h2[l] - h2[r+1]*p[r-l+1];
    }
    
    int midl, midr;
    
    bool check(int len)
    {
    	if(queryPre(midl-len+1, midl) == queryLast(midr, midr+len-1)) return 1;
    	return 0;
    }
    
    signed main(){
    	Ios;
    	cin >> n;
    	cin >> a + 1;
    	
    	p[0] = 1;
    	for(int i=1;i<=n;i++)
    	{
    		h1[i] = h1[i-1] * P + a[i];
    		p[i] = p[i-1] * P;
    	}
    	for(int i=n;i>=1;i--) h2[i] = h2[i+1] * P + a[i];
    	
    	int ans = 0;
    	for(int i=1;i<=n;i++)
    	{
    		if(queryPre(1, (i+1)/2) == queryLast(i/2+1, i))
    		{
    			if(i%2) ans += 26;
    			else ans ++;
    		}
    		else
    		{
    			midl = (i+1)/2, midr = i/2+1;
    			int l = 0, r = (i+1)/2;
    			
    			while(l < r)
    			{
    				int mid = l + r + 1>>1;
    				if(check(mid)) l = mid;
    				else r = mid - 1;
    			}
    			if(queryPre(1, midl-l-1) == queryLast(midr+l+1, i)) ans += 2;
    		}
    	}
    	cout << ans;
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    实现二:

    #include
    using namespace std;
    
    #define Ios ios::sync_with_stdio(false),cin.tie(0)
    #define fi first
    #define se second
    #define endl '\n'
    
    const int N = 2000010, mod = 1e9+7;
    int T, n, m;
    char a[N];
    unsigned long long p[N], h1[N], h2[N];
    int P = 131;
    
    int queryPre(int l, int r)
    {
    	if(l > r) return 0;
    	return h1[r] - h1[l-1]*p[r-l+1];
    }
    int queryLast(int l, int r)
    {
    	if(l > r) return 0;
    	return h2[l] - h2[r+1]*p[r-l+1];
    }
    
    int midl, midr;
    
    bool check1(int x, int n)
    {
    	if(queryPre(1, x) == queryLast(n-x+1, n)) return 1;
    	return 0;
    }
    
    signed main(){
    	Ios;
    	cin >> n;
    	cin >> a + 1;
    	
    	p[0] = 1;
    	for(int i=1;i<=n;i++)
    	{
    		h1[i] = h1[i-1] * P + a[i];
    		p[i] = p[i-1] * P;
    	}
    	for(int i=n;i>=1;i--) h2[i] = h2[i+1] * P + a[i];
    	
    	int ans = 0;
    	for(int i=1;i<=n;i++)
    	{
    		if(queryPre(1, (i+1)/2) == queryLast(i/2+1, i))
    		{
    			if(i%2) ans += 26;
    			else ans ++;
    		}
    		else
    		{
    			int l = 0, r = (i+1)/2;
    			while(l < r) //找到的是满足的最右边位置 
    			{
    				int mid = l + r + 1 >> 1;
    				if(check1(mid, i)) l = mid;
    				else r = mid - 1;
    			}
    			l ++; //需要把找到的位置+1才是第一个不满足的位置 
    			if(queryPre(l+1, i-l) == queryLast(l+1, i-l)) ans += 2;
    		}
    	}
    	cout << ans;
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    经验

    • 不要被求方案数的题目吓倒了!
    • 妙用 二分。

    记一个双哈希:

    typedef pair<int,int > PII; 
    const int mod1=201326611;
    const int mod2=402653189;
    const int N=2e6+10;
    const int Base1=131;const int Base2 = 13331;
    const int inf=0x3f3f3f;
    
    int n,m,h,T,k,x,y,c,tot;
    int pa1[N],pa2[N],pb1[N],pb2[N];
    int suma1[N],suma2[N],sumb1[N],sumb2[N];
    char s[N];
    
    void Hash1(char s[],int n){
        pa1[0] = pa2[0] = 1;
        for(int i = 1 ; i <= n ; i ++ ){
    		 pa1[i] = pa1[i-1] * Base1 % mod1;
    		 suma1[i] = (suma1[i-1]*Base1 % mod1 + s[i]) % mod1;
    		 pa2[i] = pa2[i-1] * Base2 % mod2;
    		 suma2[i] = (suma2[i-1]*Base2 % mod2 + s[i]) % mod2;			 
    	}
        for(int i = n ; i >= 1 ; i -- ){
    		sumb1[i] = (sumb1[i+1]*Base1 % mod1 + s[i]) % mod1;
    		sumb2[i] = (sumb2[i+1]*Base2 % mod2 + s[i]) % mod2;          
    	}			
    }
    PII query1(int l,int r){
    	 int tot1 = (suma1[r]-suma1[l-1]*pa1[r-l+1] % mod1 + mod1 )%mod1;
    	 int tot2 = (suma2[r]-suma2[l-1]*pa2[r-l+1] % mod2 + mod2 )%mod2;
    	 return {tot1,tot2};
    }
    PII query2(int l,int r){
    	 int tot1 = (sumb1[l]-sumb1[r+1]*pa1[r-l+1] % mod1 + mod1 )%mod1;
    	 int tot2 = (sumb2[l]-sumb2[r+1]*pa2[r-l+1] % mod2 + mod2 )%mod2;
    	 return {tot1,tot2};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    双哈希板子:

    #include
    using namespace std;
    
    #define int long long
    #define PII pair<int,int>
    /*
    	双哈希:
    	设置两个不同的取模数,当两种取模结果相同时,两个字符串才是相同的。
    	相对单哈希更安全。 
    */
    
    const int N = 200010, mod = 1e9+7;
    int T, n, m;
    char a[N];
    int h1[N], h2[N], p1[N], p2[N];
    int P = 1313131;
    int mod1 = 1e9 + 7, mod2 = 1e9+17;
    
    PII query(int l, int r)
    {
    	if(l > r) return {0, 0};
    	int ans1 = (h1[r] - h1[l-1] * p1[r-l+1] % mod1 + mod1) % mod1;
    	int ans2 = (h2[r] - h2[l-1] * p2[r-l+1] % mod2 + mod2) % mod2;
    	return {ans1, ans2};
    }
    
    signed main(){
    	cin >> n;
    	for(int i=1;i<=n;i++) cin >> a[i];
    	
    	p1[0] = p2[0] = 1;
    	for(int i=1;i<=n;i++)
    	{
    		h1[i] = (h1[i-1] * P % mod1 + a[i]) % mod1;
    		h2[i] = (h2[i-1] * P % mod2 + a[i]) % mod2;
    		p1[i] = p1[i-1] * P % mod1;
    		p2[i] = p2[i-1] * P % mod2;
    	}
    	
    	if(query(1, 3) == query(4, 6)) cout << "YES\n";
    	else cout << "NO\n";
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
  • 相关阅读:
    阿加犀AI应用案例征集活动 持续进行中!
    从零点五开始的深度学习笔记——VAE(Variational AutoEncoder) (一) 预备知识
    EMR-Jindo Spark 核心引擎优化
    【最详细】最新最全Java基础面试题(52道)
    【Voyage】GDOI 2023 旅游记 || ECHO.
    二叉树遍历
    1021 Deepest Root
    小鱼送你个URDF模板|省出来时间过七夕
    Oracle创建用户、授权视图权限
    Spring Boot 配置 jar 包外面的 Properties 配置文件
  • 原文地址:https://blog.csdn.net/Mr_dimple/article/details/126753332