ACM解题总结——HihoCoder1300

题目来源:
   HihoCoder 1300

题目要求:
   
对于一个仅由“(”和“)”两种字符构成的字符串,如果它满足下面两个条件,就说它是“合法”的:
        ① 整个字符串中的“)”和“)”的数目是相同的。
        ② 在所有该字符串的前缀中,“(”的数目一定不小于“)”的数目。
现给出一个由“(”和“)”组成的字符串,求解其中有多少个子串是合法的。

解答:
   
本题采用动态规划的思路进行解答,记给出的字符串为s[i], (i = 1, 2, ... n)。定义状态如下:
        dp[i]: 以s[i]为结尾的合法子串的数目。
计算dp[i]的方式如下:
    (1) 首先,如果s[i]是左括号,即s[i] = '(',那么:
                    dp[i] = 0
    也就是说,以左括号为结尾的子串都是不合法的。原因在于,假设有一子串:s[j], s[j+1], ..., s[i], 那么根据条件①,该子串中的左括号和右括号的数目一定是相同的,那么它的前缀:s[j], [sj+1], ..., s[i-1] 中的右括号数目一定比左括号的数目多1,这不满足条件②,因此它是不合法的。
    (2)如果s[i]是右括号,即s[i] = ')',并且它对应的左括号为s[j], 那么:
                    dp[i] = dp[j - 1] + 1
    原因在于,因为s[i]对应的左括号为s[j], 因此,整个字符串的结构一定是下面的样子:
            s[1] s[1] .... s[j-1] ( s[j+1] ... s[i-1] ) s[i+1] ... s[n]

    此时,子串:s[j], s[j+1] ... s[i-1], s[i] 一定是一个合法的子串,因为它如果不满足条件①或②,都无法使得s[i]对应的左括号是s[j],因此将s[j-1]结尾的合法子串和子串:s[j], s[j+1] ... s[i-1], s[i] 拼接起来,就是以s[i]为结尾的合法子串,另外,单独的子串:s[j], s[j+1] ... s[i-1], s[i] 也是一个以s[i]结尾的合法子串,因此:

    dp[i] = dp[j-1] + 1;
    
    (3) 如果某一个右括号s[i]没有匹配的左括号,那么,所有以它结尾的子串都是不合法的:
                    dp[i] = 0
    (4) 为了保证迭代求解过程的顺利进行,算法开始时需要一个初始的值,这个值我们设为dp[0]表示空串情况下的合法子串数目,显然:
                    dp[0] = 0。
    
    采用一个栈来计算每个右括号对应的左括号,每遇到一个左括号,就将其入栈,遇到右括号,那么当前的栈顶元素就是该右括号对应的左括号,将其出栈即可。

    最后,将dp[0], dp[1] ... dp[n]的数值累加起来,就是题目需要的答案。


输入输出格式:
    输入:
输入数据仅一行,包含一个长度为n的括号字符串。
    输出:
输出一行,表示合法的括号子串的数目。

数据范围:
     1 ≤ n ≤ 1,000,000

程序代码:

/****************************************************/
/* File        : HihoCoder1300                      */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-05-18                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/

#include<stdio.h>
#include<string.h>

/*
 * The main program.
 */
int main(void) {
	char n[1000001];
	
	scanf("%s", n);
	int length = strlen(n);
	
	int stack[length];
	int sum[length + 1];
	int top = -1;
	
	sum[0] = 0;
	int count = 0;
	
	for(int i = 0; i < length; i++) {
		if(n[i] == '(') {
			top++;
			stack[top] = i;
			sum[i + 1] = 0; 
		} else {
			if(top == -1) {
				sum[i + 1] = 0;
			} else {
				sum[i + 1] = sum[stack[top]] + 1;
				top--;
			}
		}
		count += sum[i + 1];
	}
	
	printf("%d\n", count);
	
	return 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值