On the LCS & LIS

\ (LIS \) (the longest rising sequence)

Seek length

\(dp\) - \(O(n ^ 2)\)

Dynamic programming approach

Order \ (f [i] \) represented by section \ (I \) th element end \ (the LIS \) length

则有: \(f[i] = max(f[i],f[j] + 1),(a[j] < a[i],j < i)\)

By enumerating \ (f [i] \) and \ (f [j] \) to continuously transfer state, and continuously updates the maximum enum

\(Code\):

#include<cstdio>
using namespace std;
#define maxn 10005
int f[maxn],a[maxn];
int main()
{
    int n,Max = 0;
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
    for(int i = 1;i <= n;i ++)
    {
        f[i]  = 1;
        for(int j = 1;j < i;j ++) 
            if(a[j] < a[i] && f[i] < f[j] + 1) f[i] = f[j] + 1;
    }
    for(int i = 1;i <= n;i ++) if(f[i] > Max) Max = f[i];
    printf("%d",Max);
    return 0;
}

The algorithm may determine the maximum increase in specific sequences, but increased only when required the longest sequence length, we can consider generally better \ (O (n \ lg \ n) \) approach

\ (DP \) + Fenwick tree \ (O (n \ lg \ n) \)

We noticed when we moved in the state to enumerate \ (f [j] \) of the maximum value to the transfer, we can consider using the data structure to maintain in order to optimize it, as long as it supports single most value to modify and query data range This can be done structure, block \ ((O (n \ sqrt n)) \) and Fenwick tree \ ((O (n \ lg \ n-)) \) , segment tree \ ((O (n \ lg \ n)) \) and the like will do, but because Fenwick tree is better to write, so we only explain the wording of Fenwick tree

  1. Pre-press weights then check the serial number, the sort after the biggest \ (f [j] \) to shift, but one thing to note, we find that the LIS, is a strictly increasing, so we met repeat weights when it should be placed at the end a one-time deal, or repeat back of \ (f [] \) will be able to use the same front element to shift, resulting in a final answer is wrong
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 1000007
int n,Dp[maxn],Ans,Max[maxn];
struct Node{int w,i;}A[maxn];
#define lowbit(x) ((x) & (-x))
inline bool cmp(Node A , Node B){return A.w < B.w;}
inline void Update(int Pos , int w) 
{
    for(int i = Pos;i <= n; i += lowbit(i)) 
        Max[i] = max(Max[i] , w);
}
inline int Query(int Pos) 
{
    int Ret = 0;
    for(int i = Pos ; i ; i -= lowbit(i)) 
        Ret = max(Ret , Max[i]);
    return Ret;
}
int main() 
{
    scanf("%d" , &n);
    for(int i = 1;i <= n;i ++) scanf("%d" , &A[i].w) , A[i].i = i;
    sort(A + 1 , A + 1 + n ,cmp);
    int Last = 1;//为了处理权值相同时的情况
    for(int i = 1;i <= n;i ++) //确保权值的大小关系正确
    {
        if(A[i].w != A[i - 1].w && i - 1) //处理前面权值相同的情况
        {
            for(int j = Last;j <= i - 1;j ++) Update(A[j].i , Dp[j]); 
            //如果不是到了最后再更新的话,后面重复的就会用前面重复的值来更新
            Last = i;//处理完转移过来
        }
        Dp[i] = Query(A[i].i) + 1;//转移
        Ans = max(Ans , Dp[i]);
    }
    printf("%d" , Ans);
    return 0;
}
  1. Maintenance \ (f [] \) the array, but with the right value as an array subscript, then do not need to \ (the Sort \) , the order of enumeration on it, about the size of the value we can directly find (Fenwick tree), notes that the scope is large, we can discretize
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 1000007
int n,ans,f[maxn];
struct Node{int val,num;}z[maxn]; 
#define lowbit(x) ((x) & (-x))
inline void modify(int x,int y)
{
    for(;x < maxn ;x += lowbit(x))
        f[x] = max(f[x],y);
    return ;
}
inline int query(int x)
{
    int res = 0;
    for(;x;x -= lowbit(x)) res = max(res,f[x]);
    return res;
} 
int main()
{
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++) scanf("%d",&z[i].val);
    for(int i = 1;i <= n;i ++)
    {
        int Max = query(z[i].val - 1);
        modify(z[i].val , ++ Max);
        ans = max(ans,Max);
    }
    printf("%d",ans);
    return 0;
}

Piggy-half + \ (O (n \ lg \ n) \)

Greedy approach

Maintaining a monotonous stack, the stack is then given element in accordance with the current element and the comparison element to select the optimal strategy, define \ (Stack [] \) is monotone stack, the stack elements \ (Stack [Top] \) , the current sequence of The first \ (I \) th element \ (a [i] \)

  1. If \ (stack [top] <a [it] \) then monotone can be directly pushed onto the stack
  2. If \ (stack [top] \ geqslant a [i] \) then this time is not inserted into the monotone, then we consider binary search in monotonous stack, then find the first \ (stack [j] \ geqslant a [I] \) is replaced to

Q: Why replace this greedy strategy is feasible?

A: because it does not increase the length of the stack, and so the next can have a better solution, in fact, emotional understanding is a more than one sequence, can be understood as two or more in the stack, but in the alternative after the press-fitting may be applied to each element of a sequence to contribute, as in the following example

\(Input\):

5
1 4 2 5 3

According to the above decision greedy little difficult to launch such a procedure:

  1. stack[1] = {1};
  2. stack[2] = {1,4};
  3. stack[2] = {1,2};
  4. stack[3] = {1,2,5};
  5. stack[3] = {1,2,3};

See section \ (3 \) replacing process step, the \ (4 \) becomes \ (2 \) , in fact \ (4 \) there is {1,4,5}such an increase in the longest sequence, but if there is a digital back no \ (2 \) better, after we replace, \ (4 \) is in fact involved, but because we found more superior \ (2 \) , so \ (4 \) contributions definitely better than \ (2 \) contribution to small, can replace direct swap

\(Warning\)

The \ (O (n \ lg \ n) \) algorithms can not be obtained in fact, increase the longest specific sequence, as intermediate in the process of replacing the original sequence has been disrupted to, for replacing the element I do not know whether to increase the value for the longest sequence behind, it is not possible

\(Code\):

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 100005
int a[maxn],d[maxn];
int main() 
{
    int n,len = 1;
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
    d[1] = a[1];
    for(int i = 2; i <= n; i ++)
    {
        if(d[len] < a[i]) d[++ len] = a[i];
        else *lower_bound(d + 1 , d + 1 + len , a[i]) = a[i];
    }
    printf("%d",len);
    return 0;
}

DETAILED seeking \ (the LIS \) sequence

This problem \ (dp - O (n ^ 2) \) The idea can be applied. Correspondingly, we can add on a Fenwick tree to optimize the time complexity of the algorithm to \ (O (n \ lg \ n) \) , by the end of recording a corresponding sequence of the precursor to optimize the space complexity of the algorithm to \ (O (the n-) \) , and then do the very, Fenwick tree because there are two ways for writing, authors One approach is to write only solution, another solution allows the reader to think for themselves, but it is best to look at discrete

#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 1000007
int n,Dp[maxn],Ans,Max[maxn],Max_num[maxn],pre[maxn],num = 0,a[maxn];
struct Node{int w,i;}A[maxn];
#define lowbit(x) ((x) & (-x))
inline bool cmp(Node A , Node B){return A.w < B.w;}
inline void Update(int Pos , int w,int Num) 
{
    for(int i = Pos;i <= n; i += lowbit(i)) 
        if(Max[i] < w) Max[i] = w,Max_num[i] = Pos;
    return ;
}
inline int Query(int Pos) 
{
    int Ret = 0;num = 0;
    for(int i = Pos ; i ; i -= lowbit(i)) 
        if(Ret < Max[i]) Ret = Max[i],num = Max_num[i];
    return Ret;
}
inline void Output(int first)
{
    if(!first) return ;
    else Output(pre[first]);
    printf("%d ",a[first]);
    return ;
}
int main() 
{
    scanf("%d" , &n);
    for(int i = 1;i <= n;i ++) scanf("%d",&a[i]),A[i].w = a[i],A[i].i = i;
    sort(A + 1 , A + 1 + n ,cmp);
    int Last = 1,first = 0;
    for(int i = 1;i <= n;i ++)
    {
        if(A[i].w != A[i - 1].w && i - 1)
        {
            for(int j = Last;j <= i - 1;j ++) Update(A[j].i , Dp[j],j); 
            Last = i;
        }
        Dp[i] = Query(A[i].i) + 1;
        pre[A[i].i] = num;
        if(Ans < Dp[i]) Ans = Dp[i],first = A[i].i;
    }
    printf("%d\n" , Ans);
    Output(first);
    return 0;
}

\ (LCS \) (the longest common subsequence)

Seek length

\(O(nm)\)

Dynamic programming approach, in fact, the principle is very simple, is to see whether the problem is matching the current position, then according to the state to divert

With length \ (n-\) string \ (S \) and a length of \ (m \) string \ (T \) , with \ (f [i, j] \) represents \ (S \) string before \ (I \) characters and \ (T \) before the string \ (J \) characters \ (the LCS \) there is:

\(f[i,j] = max(f[i - 1,j],f[i,j - 1],(f[i - 1,j - 1] + 1) * [S_i = T_j])\)

That, in any case, there must be

\(f[i,j] = max(f[i - 1,j],f[i,j - 1])\)

Discuss the special circumstances: when \ (S_i = T_j \) when

\(f[i,j] = max(f[i,j],f[i - 1,j - 1] + 1)\)

So we can launch this result, then \ (f [n, m] \) is the final answer

Since \ (f [i] \) will always be from \ (f [i - 1] \) of this dimension transfer over, so we can consider rolling array to optimize spatial complexity

\(Code\):

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

const int maxn = 5e3 + 7;

int n,m,tmp;
char a[maxn],b[maxn];
int f[2][maxn];

int main()
{
    scanf("%s",a + 1);
    n = strlen(a + 1);
    scanf("%s",b + 1);
    m = strlen(b + 1);
    int now = 0;
    for(int i = 0;i <= n;i ++)
    {
        now ^= 1;
        for(int j = 1;j <= m;j ++)
        {
            f[now][j] = max(f[now ^ 1][j],f[now][j - 1]);
            tmp = f[now ^ 1][j - 1] + 1;
            if(a[i] == b[j] && f[now][j] < tmp) f[now][j] = tmp;
        }
    }
    printf("%d",f[now][m]);
    return 0;
}

\ (O (n \ lg \ n) \)

In fact, I talk about \ (LIS \) for a reason, we can try to explore the link between these two issues

If I can put the original sequence into another sequence and then find \ (LIS \) to obtain \ (LCS \) just fine, then how to convert these two problems?

We can easily find, \ (LIS \) is used to find the longest rising sequence, so when a sequence is in ascending order, the other when a sequence out of order, seeking two sequences \ (LCS \) is in fact seeking scrambled sequence \ (the LIS \) , because there is an ascending sequence, a sequence must exist, the longest rising ascending sequence with the sequence scrambled sequence has the longest common subsequence, so out of order can

So we start input \ (A \) when the sequence will be one by which elements of a sequence number, then the B sequence to the corresponding element in the encoding \ (A \) sequence number is \ ( B \) in the sequence of elements (a \) \ position in the sequence

For example,

1 5 4 3 2
5 3 1 2 4

After the number is

1 2 3 4 5
2 4 1 5 3

Then to seek to change the \ (B \) sequence \ (the LIS \) like, that is the answer 2 4 5, a length \ (3 \)

\(Warning\)
  1. When two sequences have different elements, dealing with the different elements to be removed, otherwise \ (LIS \) may contain elements of another sequence does not lead to erroneous answers

  2. When there are two repeating elements in the sequence can not be treated by this method

For example:

abacc
cbabb

We can find these two sequences \ (LCS \) is abor balength 2

But in the course of treatment assignment in a little trouble

abacc
cbabb
1 2 3 4 5
5 2 3 2 2

We found duplicate numbers in the sequence of the processed, because for the first time to the (A \) \ when the elements assignment, we assigned a value twice for duplicate elements, so he led the sequence the number of positions corresponding to a plurality, in the process will overwrite

Then you might say: I do not overwrite it wants

Then You \ (naive \) idea might be wasted, because in some cases, no matter how you assign, in fact, will not necessarily be a sequence in ascending order, this time seeking another sequence \ (LIS \) does not make sense, because you sequence itself is not ascending, I ask again ascending to nothing to do with another sequence

  1. The algorithm can not determine the specific \ (the LCS \) sequence, can not be used if the subject requires a specific sequence of the algorithm, the same reason \ (the LIS \) a \ (n \ lg \ n \ ) approach

\(Code\):

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 100005
int a[maxn],b[maxn];
int d[maxn],stack[maxn],len = 0;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 0;i < n;i ++) 
    {
        scanf("%d",&a[i]);
        d[a[i]] = i + 1;
    }
    for(int i = 0;i < n;i ++) 
    {
        scanf("%d",&b[i]);
        b[i] = d[b[i]];
    }
    stack[1] = b[0],len = 1;
    for(int i = 0;i < n;i ++)
    {
        if(stack[len] < b[i]) stack[++ len] = b[i];
        else *upper_bound(stack + 1,stack + 1 + len,b[i]) = b[i];
    }
    printf("%d",len);
    return 0;
}

DETAILED seeking \ (the LCS \) sequence

We can learn from it consider (LIS \) \ wording, and maximum values recorded at the beginning of the precursor, and then recursively reverse output, but this time we \ (dp [] \) can not be used to optimize the data structure, so our algorithm is \ (O (nm) \) , the specific code reader think for themselves

\ (LCIS \) (the longest common subsequence rise)

We can combine the above \ (LIS \) and \ (LCS \) is thought to ponder this question

\(LIS:\)

Order \ (f [i] \) represented by section \ (I \) th element end \ (the LIS \) length

则有: \(f[i] = max(f[i],f[j] + 1),(a[j] < a[i],j < i)\)

\(LCS:\)

With length \ (n-\) string \ (S \) and a length of \ (m \) string \ (T \) , with \ (f [i, j] \) represents \ (S \) string before \ (I \) characters and \ (T \) before the string \ (J \) characters \ (the LCS \) there is:

\(f[i,j] = max(f[i - 1,j],f[i,j - 1],(f[i - 1,j - 1] + 1) * [S_i = T_j])\)

I think we can find a little combination:

When \ (f [i, j] \) represents \ (A \) pre-sequence \ (I \) elements and \ (B \) pre-sequence \ (J \) elements \ (LCIS is \) , \ ( T \) represents \ (LCIS is \) end position of the element, there are:

\ (F [i, j] = f [i - 1, j], A_i \ ne B_j \)

\(f[i,j] = max(f[i - 1,j],f[i - 1,t] + 1),A_i = B_j\)

Also found that \ (f [i] \) of this dimension every time from \ (f [i - 1] \) This dimension transfer over, so we can use it to get rolling array optimization:

\ (F_i \) representative of a sequence \ (A \) before \ (I \) th element of the sequence \ (B \) of \ (LCIS is \) , \ (T \) ending location,

\(f_j = f_t + 1,A_i = B_j\)

Calculating \ (LCIS is \) during length, we can drop the recording precursor is then output on the particular sequence, it can be used \ (O (nm) \) time complexity calculating \ (LCIS is \) length \ (LCIS is \) of the specific sequence

\(Code\):

#include<cstdio>
using namespace std;
#define maxn 505
int a[maxn],b[maxn],f[maxn],pos[maxn];
void output(int x)
{
    if(!x) return;
    output(pos[x]);
    printf("%d ",b[x]);
}
int main() 
{
    int n,m,Max = 0;
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++) scanf("%d",&a[i]);
    scanf("%d",&m);
    for(int i = 1;i <= m;i ++) scanf("%d",&b[i]);

    for(int i = 1,t = 0;i <= n;i ++,t = 0)
        for(int j = 1;j <= m;j ++) 
        {
            if(a[i] == b[j]) f[j] = f[t] + 1,pos[j] = t;//f[j] 的结尾
            if(a[i] > b[j] && f[t] < f[j]) t = j;// 保证t在结尾位置
        }
    for(int i = 1;i <= m;i ++)
        if(f[i] > f[Max]) Max = i;
    printf("%d\n",f[Max]);
    output(Max);
    return 0;
}

So far, we have discussed \ (LIS \) , \ (LCS \) and \ (LCIS \) three basic types of dynamic programming, the connection between the algorithm and the algorithm can be seen


Acknowledgments

  • Thanks to some high-quality solution to a problem on the Los Valley, I found a lot of ideas from problem solutions in many teachable
  • \ (Liang \) \ (Shine \) \ (Sky \) Gangster advice

Guess you like

Origin www.cnblogs.com/eqvpkbz/p/11619653.html