戴克斯特拉+斐波那契堆

//(只贴代码,不做讲解,欢迎纠正)

//用斐波那契堆实现优先队列 
//复杂度O(VlogV+E)
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#define INF 0x3F3F3F3F
#define MAXSIZE 1001
using namespace std;

int dis[MAXSIZE];
int path[MAXSIZE];
void dijkstra(int v, int start);
void pathout(int from, int to);
void graphin(int v, int e);

struct node
{
    int front, weight;
    node* next;
}graph[MAXSIZE];

template<typename datatype>
struct fib_node
{
    fib_node()
    {
        degree = 0;
        parent = NULL;
        left = NULL;
        right = NULL;
        child = NULL;
        mark = false;
    }
    int degree;
    datatype key;
    fib_node<datatype> *parent, *left, *right, *child;
    bool mark;
};


//最小堆 
template<typename datatype>
class fib_heap
{
public:
    fib_heap()
    {
        ssize = 0; root = NULL;
    }

    void push(fib_node <datatype>*x)
    {
        x->mark = false;
        if (ssize)
        {
            x->left = root;
            x->right = root->right;
            x->right->left = x;
            root->right = x;
            if (x->key<root->key)
            {
                root = x;
            }
        }
        else
        {
            root = x;
            x->right = x;
            x->left = x;
        }
        ssize++;
    }

    void push(datatype k)
    {
        fib_node <datatype>*x = new(fib_node<datatype>);
        x->key = k;
        this->push(x);
    }

    datatype top()
    {
        if (ssize)
        {
            return root->key;
        }
        throw string("in top(), the heap is empty");
    }

    void unio(fib_heap<datatype> *h)
    {
        if (h->ssize == 0) return;
        if (this->ssize == 0)
        {
            root = h->root;
            this->ssize = h->ssize;
            return;
        }
        fib_node <datatype>*tmp = this->root->right;
        this->root->right = h->root->right;
        tmp->left = h->root;
        h->root->right = tmp;
        this->root->right->left = this->root;
        this->ssize += h->ssize;
        h->root = NULL;
        h->ssize = 0;
    }

    int size()
    {
        return ssize;
    }

    void pop()
    {
        if (!ssize)
        {
            throw string("in pop(), the heap is empty");
        }
        fib_node<datatype> *now = root->child;
        while (now)
        {
            fib_node <datatype> *nex = now->right;
            now->right = root->right;
            now->left = root;
            root->right = now;
            now->right->left = now;
            now->parent = NULL;
            if (nex->parent == NULL)    break;
            now = nex;
        }
        if (ssize == 1)
        {
            delete(root);
            root = NULL;
        }
        else
        {
            fib_node <datatype> *tmp = root;
            root->left->right = root->right;
            root->right->left = root->left;
            root = root->right;
            delete(tmp);
            conslidate();
        }
        ssize--;
    }
    void decrease(fib_node<datatype> *x, datatype k)
    {
        if (x->key<k) throw(string("in decrease: the key of x is smaller than the new key!"));
        x->key = k;
        fib_node <datatype> *y = x->parent;
        if (y != NULL&&x->key<y->key)
        {
            cut(x, y);
            cascading(y);
        }
        if (x->key<root->key)
        {
            root = x;
        }
    }
private:
    int ssize;
    void conslidate()
    {
        int D = -1;
        int tmp = ssize;
        while (tmp)
        {
            tmp >>= 1;
            D++;
        }
        //////////////////////////////////
        fib_node <datatype>**deg = new(fib_node<datatype>*[D + 1]);
        memset(deg, 0, sizeof(deg)*(D + 1));
        fib_node <datatype> *theend = root->left;
        fib_node <datatype> *now = root;
        while (true)
        {
            int d = now->degree;
            fib_node <datatype> *x = now;
            fib_node <datatype> *nex = now->right;
            while (deg[d])
            {
                fib_node <datatype> *y = deg[d];
                if (y->key<x->key)
                {
                    swap(x, y);
                }
                //将y节点接到x节点的下面 
                y->right->left = y->left;
                y->left->right = y->right;
                y->parent = x;
                y->mark = false;
                if (x->child == NULL)
                {
                    x->child = y;
                    y->right = y;
                    y->left = y;
                }
                else
                {
                    y->right = x->child->right;
                    y->left = x->child;
                    x->child->right = y;
                    y->right->left = y;
                }
                x->degree++;
                deg[d++] = NULL;

            }
            deg[d] = x;
            if (now == theend) break;
            now = nex;
        }
        root = NULL;
        for (int i = 0; i <= D; i++)
        {
            if (deg[i])
            {
                if (root == NULL)
                {
                    root = deg[i];
                    root->right = root;
                    root->left = root;
                }
                else
                {
                    deg[i]->left = root;
                    deg[i]->right = root->right;
                    root->right = deg[i];
                    deg[i]->right->left = deg[i];
                    if (deg[i]->key<root->key)
                    {
                        root = deg[i];
                    }
                }
            }
        }
    }
    void cut(fib_node<datatype> *x, fib_node<datatype> *y)
    {
        if (x->right == x)
        {
            y->child = NULL;
        }
        else
        {
            x->right->left = x->left;
            x->left->right = x->right;
            //防止y->child恰好为x 
            y->child = x->right;
        }
        y->degree--;
        x->mark = false;
        x->right = root->right;
        x->left = root;
        root->right = x;
        x->right->left = x;
        x->parent = NULL;
    }
    void cascading(fib_node<datatype> *x)
    {
        fib_node <datatype> *y = x->parent;
        if (y)
        {
            if (!x->mark)
            {
                x->mark = true;
            }
            else
            {
                cut(x, y);
                cascading(y);
            }
        }
    }
    fib_node <datatype> *root;
};

int main()
{
    int start, v, e;
    cout << "请输入图(节点编号从0开始):" << endl;
    cin >> v >> e;
    graphin(v, e);
    cout << "请输入起点:" << endl;
    cin >> start;
    dijkstra(v, start);
    for (int i = 0; i<v; i++)
    {
        if (i == start) continue;
        if (dis[i] == INF) continue;
        cout << "distance(" << start << "," << i << ") = " << dis[i]
            << " the path is: ";
        pathout(start, i);
        cout << endl;
    }
    return 0;
}

void graphin(int v, int e)
{
    memset(graph, 0, sizeof(graph));
    for (int i = 0; i<e; i++)
    {
        int from, to, w;
        cin >> from >> to >> w;
        node *tmp = graph[from].next;
        //用nex代替graph[from].next,节约时间 
        node *nex = new(node);
        graph[from].next = nex;
        nex->front = to;
        nex->weight = w;
        nex->next = tmp;
    }
}

struct point
{
    point(int w_=0, int u_=0)
    {
        w = w_;
        u = u_;
    }
    int w, u;
};

bool operator<(point a, point b)
{
    return a.w < b.w;
}

//储存每个节点在堆中的位置,用于快速实现decrease-key操作 
fib_node <point> *pinheap[MAXSIZE];

void dijkstra(int v, int start)
{
    //bool vis[v];
    bool *vis = new(bool[v]);
    fib_heap <point> unvis;
    int sizeunvis = v;
    memset(dis, 0x3F, sizeof(dis));
    memset(path, 0xFF, sizeof(path));
    memset(vis, 0x00, sizeof(vis)*v);
    dis[start] = 0;
    for (int i = 0; i<v; i++)
    {
        pinheap[i] = new(fib_node<point>);
        pinheap[i]->key = point(dis[i],i);
        unvis.push(pinheap[i]);
    }
    while (unvis.size()>0)
    {
        point heaptop = unvis.top();
        unvis.pop();
        int pmin = heaptop.u;
        vis[pmin] = true;
        for (node *p = graph[pmin].next; p != NULL; p = p->next)
        {
            int f = p->front;
            int sum = dis[pmin] + p->weight;
            if (dis[f]>sum)
            {
                dis[f] = sum;
                path[f] = pmin;
                if (!vis[f])
                {
                    unvis.decrease(pinheap[f], point(dis[f],f));
                }
            }
        }
    }
}

void pathout(int from, int to)
{
    if (from == to)
    {
        cout << from;
        return;
    }
    pathout(from, path[to]);
    cout << "->" << to;
}

HDU 4821 (String) 详解

题目

题目描述
Given a string S and two integers L and M, we consider a substring of S as “recoverable” if and only if
  (i) It is of length M*L;
  (ii) It can be constructed by concatenating M “diversified” substrings of S, where each of these substrings has length L; two strings are considered as “diversified” if they don’t have the same character for every position.
Two substrings of S are considered as “different” if they are cut from different part of S. For example, string "aa" has 3 different substrings "aa", "a" and "a".
Your task is to calculate the number of different “recoverable” substrings of S.
输入
The input contains multiple test cases, proceeding to the End of File.
The first line of each test case has two space-separated integers M and L.
The second line of each test case has a string S, which consists of only lowercase letters.
The length of S is not larger than 10^5, and 1 ≤ M * L ≤ the length of S.
输出
For each test case, output the answer in a single line.
输入样例
3 3
abcabcbcaabc
输出样例
2

解析

首先应明确一下题目中“don’t have the same character for every position”的含义,这其实就是说两个字符串不相同,即s1!=s2;并不是说两个字符串的任意两位都不同。
容易想到,我们可以将字符串S分割成若干个长度为L的子串,如,样例中的abcabcbcaabc可分割为:/abc/abc/bca/abca/bca/bcb/caa/bcab/cab/cbc/aab/c,共3种方案,我们以第一个子串的实际长度来区分这几种方案,则这3种方案可分别记作lstart=0, 1, 2。分割后我们可以分3种情况,对于每一种情况,从第一个子串开始扫描,处理到第M个子串时,判断已处理过的子串是否有重复,如果没有,则将答案加一,处理第M+1个子串时,可以考虑将第一个子串从“已处理的子串”中删除,以免重复处理。如此做,可以保证每个子串只被处理一次,而子串数等于S.length()-L+1,算法有可能在线性时间内实现。但存在以下难点:
1.        O(1)O(log2n)的时间内判断两个字符串是否相等;
2.        O(1)O(log2n)的时间内判断一个子串是否已被处理过。

第二个难点容易解决,只需用红黑树维护已处理过的字符串即可在O(log2n)的时间内实现;而第一个难点的解决相对困难,我们需要在O(1)的摊还时间内获得一个子串的散列值。
(这里记S.length()=len)考虑预处理出以i开头长度为L的字符串的散列值,储存在全局数组hashes[100001]中。我们采用hash(S)=S[0]×330+S[1]×331+S[2]×332+…+S[len-1]×33len-133可以替换成其他数字,但不保证替换后散列不会对撞)来计算字符串的散列。首先将33i预处理储存在数组exp[len]中,我们希望将hashes[i]表示成类似于hashes[i]-hash[i+L]这样的类似于后缀和的形式,于是我们令数组sum[i]=33*sum[i+1]+S[i]-97’a’==97),而sum[L]=0。为了方便讲解我们以字符串abcabc为例:
S[i]
a
b
c
a
b
c
exp[i]
1
33
332
333
334
335
sum[i]
2×335+334+2×332+33
2×334+333+2×33+1
2×333+332+2
2×332+33
2×33+1
2
hashes[i]
33+2×332
1+2×33
2+332
33+2×332


观察发现hashes[i]实际上等于sum[i]-exp[L]*sum[i+L]
评注:这种方法是计算字符串散列值的高效算法。
值得注意的是这一系列计算远超出了unsigned long long的范围,那么是否应该用高精度呢?答案是否定的:当CPU的运算器得到的结果超过了寄存器的宽度后,数字的高位将被舍去,这实际上相当于完成了一次取模运算,而a mod c-b mod c=(a-b) mod c (a, b, c 0)因此不必担心溢出的问题。

最终我们的算法的时间复杂度为O(nlog2n+n)

参考程序

#include <iostream>
#include <string>
#include <map>
using namespace std;
unsigned long long hashes[100001];
 
//预处理出所以长度为l的字符串的散列值 
void gethash(string &s,int l)
{
    int len=s.length();
    unsigned long long exp[len+1];
    exp[0]=1;
    for(int i=1;i<=len;i++)
    {
        exp[i]=exp[i-1]*33;
    }
    unsigned long long sum[len+1];
    sum[len]=0;
    for(int i=len-1;i>=0;i--)
    {
        sum[i]=33*sum[i+1]+s[i]-97;
    }
    for(int i=0;i<=len-l;i++)
    {
        hashes[i]=sum[i]-sum[i+l]*exp[l];
    }
}
 
int main()
{
    string str;
    int m,l,len,ans;
    //cnt[h]表示一个散列为h的字符串出现的次数
    //cnt[h]的值均为10时,cnt.size()=m,
    //由此可判断每个子串是否只出现过一次 
    map <unsigned long long,int> cnt;
    while(cin>>m>>l)
    {
        cin>>str;
        len=str.length();
        ans=0;
        gethash(str,l);
        //枚举分割方案 
        for(int n=0;n<l&&n<=len-l*m;n++)
        {
            //cout<<"in case "<<n<<endl;
            cnt.clear();
            //sub表示已处理的子串的个数 
            int sub=0;
            //枚举这种方案下的子串
            //(通过枚举起始点实现) 
            for(int i=n;i<=len-l;i+=l)
            {
                cnt[hashes[i]]++;
                sub++;
                //cout<<"i="<<i<<" sub="<<sub<<endl;
                if(sub==m)
                {
                    //cout<<"sub==m cnt.size()="<<cnt.size()<<endl;
                    if(cnt.size()==m)
                    {
                        ans++;
                    }
                    //cout<<"i-l*m+l="<<i-l*m+l<<endl;
                    //将第一个处理子串从已处理的子串中去除,
                    //使已处理的子串的个数始终小于等于m
                    cnt[hashes[i-l*m+l]]--;
                    if(cnt[hashes[i-l*m+l]]==0)
                    {
                        cnt.erase(hashes[i-l*m+l]);
                    }
                    sub--;
                }
            }
             
        }
        cout<<ans<<endl;
    }
    return 0;
}