字串最大和求解

http://acm.hdu.edu.cn/showproblem.php?pid=1003

ACM的一道题。之前在《算法导论》中看到介绍寻找最大字串的方法。书中介绍了一种用递归来求解的方法。另外提出可以最O(n)时间内找到最大和字串。今天主要说下自己的思路。

1、暴力计算方法。

如字符串: 5 6 -1 5 4 -7

以5开头,要计算(5)(5,6)(5,6,-1)(5,6,-1,5)(5,6,-1,5,4)(5,6,-1,5,4,-7)这6个字串的最大值。

以6开头,计算5个,同理类推,对于输入为n,计算的字串个数为 n(n+1)/2,所以复杂度是O(n*n).

2、O(n)求最大值的方法。

基本原理:对于一个已经知道最大子串(last)的字符串(A)(注:记字符串为A,最大字串为last)。加入一个新元素cur,形成新的字符串B。此时考虑B的最大子串。

已知:A的最大子串为last,B=A+cur (在A的末尾加上cur,形成新的字符串B)

求: B的最大子串。

当我们从字符串的左边第一个元素开始应用程序时,输入的字符串为S

第一个元素: A = S[0], last = S[0]

添加新元素S[1]: B = A + cur = S[0] + S[1].

我们需要考虑last(最大子串)的更新规律。观察由A到B过程中,last的更新。

用pos_b表示最大子串last的起始位置,pos_e表示最大子串last的终止位置

1、将cur与last连接起来,求出一个候选值candidate1。由于last与cur中间可能不是连续的。

例如: A = (5,6,-1), 可知 last = 11,pos_e = 1, pos_e = 2

加入cur = 5. 则 B = (5,6,-1,5), 如果要把 cur 和 last 连接起来,则需要记录中间的元素,如 -1

定义 need1 表示 cur 和 last 的中间元素,可以得到

need1 += cur;

candidate1 = last + need1

2、第二个候选值(可能成为最大子串的连续子串)为cur和之前的和为整数组成的子串。这个比较难理解。看下面的例子。

5 -6 3 7

(由1中计算的candidate1 = 9)

最大子串为(3,7)所以处理 cur = 7 时,需要去考虑cur之前的元素是否最以cur为结尾的子串有贡献(与1中不同的是,此时cur不一定与last连接起来。)

定义 need2 表示cur之前元素对cur结尾子串的贡献。可知只有当need2为正数时,有贡献,为0或负数时舍去。

if(need2 > 0)

  need2 += cur; //need2有贡献

else

  need2 = cur; //无贡献,则将need2设置为cur,表示抛弃之前的数据。

所以可得2分析的候选子串candidate2 = need2

3、第三个候选子串为last,没有加入cur之前的子串。如果cur<0, 则 B中最大的子串依然是A中的最大子串last. candidate3 = last.

所以。最大字串的更新,last = max(candidate1,candidate2,candidate3).

源代码如下:

注意每次更新完最大值后,需要要更新记录最大子串的区间的下标 pos_b, pos_e

可能有多重解。控制 candidate1,candidate2,candidate3 相等时,如何更新最大子串的下标,可以得到不同的解。(最长、最短、最靠左、最靠右等。)

#include <iostream>
using namespace std;
//已知前一个字串的最大和last,加入当前cur,可能的情况如下
// 1、last <= 0, 放弃last, 则cur或cur之前的元素和为当前字串最大值
// 2、last > 0, cur < 0; 将last作为当前最大值,如果cur > 0
//     需要考虑将cur连接上,或cur之前部分的和
// 注:字符最短时、最前
// if(last >= can1 && last >= can2)
// else if(can1 > last && can1 > can2)
// else if(can2 > last && can2 >= can1)
// 字符最长时,最前
// if(last > can1 && last >= can2)
// else if(can1 >= last && can1 >= can2)
// else if(can2 > last && can2 > can1)
// can1最长,can2和last长短需要另外计算,can2靠后,last靠前
int _tmain(int argc, _TCHAR* argv[])
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int last,cur,need1,need2;
    int can1,can2;
    int seq,num;
    int pos_b,pos_e,pos_h;
    cin>>seq;
    for(int i = 1; i <= seq; i++)
    {
        cin>>num;
        cin>>cur;
        last = cur; need1 = 0; need2 = 0;
        pos_b = 1; pos_e = 1,pos_h = 1;
        for(int j = 2; j <= num; j++)
        {
            cin>>cur;
            need1 += cur;
            if(need2 + cur > cur)
            { need2 += cur; }
            else
            { need2 = cur; pos_h = j; }
            can1 = last + need1;
            can2 = need2;
            //update
            if(last > can1 && last >= can2) //最短
            {
                //nothing to update
            }
            else if(can1 >= last && can1 >= can2)
            {
                need1 = 0; 
                need2 = 0; 
                last = can1; 
                pos_e = j;
            }
            else if(can2 > last && can2 > can1) //更新最大值和下标
            {
                need1 = 0;
                need2 = 0;
                last = can2;
                pos_b = pos_h;
                pos_e = j;
            }
        }
        if(i == seq)
        {
            cout<<"Case "<<i<<":"<<endl;
            cout<<last<<" "<<pos_b<<" "<<pos_e<<endl;
        }
        else
        {
            cout<<"Case "<<i<<":"<<endl;
            cout<<last<<" "<<pos_b<<" "<<pos_e<<endl<<endl;
        }
    }
    return 0;
}

 

 

 

 

 

 

 

 

 

posted on 2014-04-15 15:23  themo  阅读(1040)  评论(0编辑  收藏  举报

导航