284. Peeking Iterator

1.题目要求实现一个窥探的功能,正常的迭代器,可以输出当前值和使用next指向下一个值。而题目要求我们输出下一个值,但是不移动指针。

2.当使用peek时,利用this指针重建了一个迭代器,在这个这个新的迭代器中执行next操作并返回值,即可不影响原来的迭代器。

Given an Iterator class interface with methods: next() and hasNext(), design and implement a PeekingIterator that support the peek() operation — it essentially peek() at the element that will be returned by the next call to next().


Here is an example. Assume that the iterator is initialized to the beginning of the list: [1, 2, 3].

Call next() gets you 1, the first element in the list.

Now you call peek() and it returns 2, the next element. Calling next() after that still return 2.

You call next() the final time and it returns 3, the last element. Calling hasNext() after that should return false.

AC代码:

// Below is the interface for Iterator, which is already defined for you.
// **DO NOT** modify the interface for Iterator.
class Iterator {
    struct Data;
	Data* data;
public:
	Iterator(const vector<int>& nums);
	Iterator(const Iterator& iter);
	virtual ~Iterator();
	// Returns the next element in the iteration.
	int next();
	// Returns true if the iteration has more elements.
	bool hasNext() const;
};


class PeekingIterator : public Iterator {
public:
	PeekingIterator(const vector<int>& nums) : Iterator(nums) {
	    // Initialize any member here.
	    // **DO NOT** save a copy of nums and manipulate it directly.
	    // You should only use the Iterator interface methods.
	    
	}
    // Returns the next element in the iteration without advancing the iterator.
	int peek() {
        if(hasNext())
        {
            Iterator same(*this);//复制一个迭代器
                return same.next();//输入这个迭代器的下一个值
        }
	}

	// hasNext() and next() should behave the same as in the Iterator interface.
	// Override them if needed.
	int next() {
        return Iterator::next();
	}

	bool hasNext() const {
        return Iterator::hasNext();
	}
};

 

315. Count of Smaller Numbers After Self(方法二:使用MergeSort)

使用树状数组求解:http://maybi.cn/wp_siukwan/?p=850

1.这道题目跟求逆序对的题目类似,但是求逆序对的题目只需要求出总的逆序对数量即可,而这道题目要求求出每个数的右边比它小的数的个数。

2.按照逆序对的思想去编写的时候会发现,mergeSort中会有一个交换赋值的过程,导致我们不能简单的使用用一个数组根据下标去记录当前数的右边比它小的数的个数,因为这个数排序后,不在原下标位置。

3.所以需要设计一个数据结构,这个数据结构记录了原始数组的val,原始数组的idx,以及所求值cnt,然后对这个新的数据结构数组进行mergeSort,同时进行统计。除去复制和复制的O(n)时间复杂度,剩下的就是MergeSort的时间复杂度O(nlogn),所以时间复杂度为O(nlogn),空间复杂度为O(n)。

AC代码:

class Solution {
public:
    vector<int> countSmaller(vector<int>& nums) {
        vector<node> num(nums.size());
        for(int i=0;i<nums.size();++i)
        {
            num[i].idx=i;
            num[i].cnt=0;
            num[i].val=nums[i];
        }
        vector<node> tmpNums(nums.size());
        
        Msort(num,tmpNums,0,num.size()-1);
        
        vector<int> result(nums.size());
        for(int i=0;i<num.size();++i)
        {
            result[num[i].idx]=num[i].cnt;
        }
        
        return result;
    }
    struct node{
        int val;//值
        int cnt;//右边比它大的数
        int idx;//原来的idx
        node():val(0),cnt(0),idx(0){};
    };
    void Merge(vector<node>&nums,vector<node>&tmpNums,int l,int k,int r)
    {
        int i=l,j=k+1;
        int idx=l;
        while(i<=k&j<=r)
        {
            if(nums[i].val<=nums[j].val)
            {//填充左边的数,证明之前填充的右边的数比这个数小,我们需要查询之前右边填充了多少个数字
                nums[i].cnt+=j-(k+1);
                tmpNums[idx++]=nums[i++];
            }
            else
                tmpNums[idx++]=nums[j++];
        }
        while(i<=k)
        {
            nums[i].cnt+=j-(k+1);
            tmpNums[idx++]=nums[i++];
        }
        while(j<=r)
            tmpNums[idx++]=nums[j++];
        
        for(i=l;i<=r;++i)
        {
            nums[i]=tmpNums[i];
        }
    }
    
    void Msort(vector<node>&nums,vector<node>&tmpNums,int l,int r)
    {
        if(l>=r) return ;
        
        int k=(l+r)/2;
        Msort(nums,tmpNums,l,k);
        Msort(nums,tmpNums,k+1,r);
        Merge(nums,tmpNums,l,k,r);
    }
};

 

 

338. Counting Bits(Medium)

题目要求给出一个数num,求出小于等于num的数在二进制表示时个位上1的总和。例如给出3,小于等于三的数有0、1、2、3,0包含0个1,1包含1个1,2包含1个1,3包含2个1,所以答案应该是{0,1,1,2}。

我们可以通过统计每个数字上的1求出结果,这样的时间复杂度就为O(n*sizeof(integer))。

其实还有更好的办法,我们来分析一下,我们先列出部分数的二进制表示:
0
1
10
11
100
101
110
111
1000

我们可以看出,第二个数10有一个一,是从第一个数0上多加一个1得到,第三第四个数分别是第一第二个数加上1得到。而第五、六、七、八个数(100,101,110,111)是第一、二、三、四个数(0,1,10,11)加上1得到。

所以我们发现,可以通过已经生成的数来帮助生成后面的数,而上面分析的加1,其实是进位而来的,我们发现每当经过2^n后,一个数的二进制表示就会进1位,意味着前面的数在最高位加1可以得到后面的数,而回归题目,求1的个数,我们先求出前面数的1个数,后面的数就可以通过加1得到。

所以总体思想是,我们额外使用一个指针idx,指向前面的数,然后num[idx]+1为当前数的1的个数。那么问题就在于idx什么时候回到起点(0)?就是当经过2^n后,令idx为0。

 

Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1’s in their binary representation and return them as an array.

Example:
For num = 5 you should return [0,1,1,2,1,2].

Follow up:

  • It is very easy to come up with a solution with run time O(n*sizeof(integer)). But can you do it in linear time O(n) /possibly in a single pass?
  • Space complexity should be O(n).
  • Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language.

AC代码:

class Solution {
public:
    vector<int> countBits(int num) {
        int idx=0,n2=1;
        vector<int> result;
        result.push_back(0);
        for(int i=1;i<=num;++i)
        {
            if(i==n2)
            {
                idx=0;
                n2*=2;
            }
            result.push_back(result[idx++]+1);
        }
        return result;
    }
};

 

字符串运用-密码截取(manacher)

1.使用manache解法,主要把数组进行填充,每个字符空隙填充’#’,然后再在开头填充’$’;

2.增加辅助变量mx(最大回文的右边界),id(拥有最大回文右边界的字符index),接着分别考虑情况mx>i和mx<=i;最后的结果为max(p[i]-1).

Catcher是MCA国的情报员,他工作时发现敌国会用一些对称的密码进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解。比如进行下列变化 ABBA->12ABBA,ABA->ABAKK,123321->51233214 。因为截获的串太长了,而且存在多种可能的情况(abaaab可看作是aba,或baaab的加密形式),Cathcer的工作量实在是太大了,他只能向电脑高手求助,你能帮Catcher找出最长的有效密码串吗?
输入描述:
输入一个字符串
输出描述:
返回有效密码串的最大长度
输入例子:

ABBA
输出例子:
4

AC代码:

#include<string>
#include<iostream>
#include<vector>

using namespace std;

int manacher(string tmp)
{
    //插入字符串,得到一个新串
    string s="$";
    for(int i=0;i<tmp.size();++i)
    {
        s+="#";
        s+=tmp[i];
    }
    s+="#";
    
    int id=0;//拥有最大回文边界的位置
    int mx=0;//最大的回文边界
    vector<int> p(s.size(),0);
    
    for(int i=1;i<s.size();++i)
    {
        //根据以前的数据计算出暂时最大的p[i]
        if(mx>i)
        {
            p[i]=min(p[2*id-i],mx-i);
        }
        else
            p[i]=1;
        
        //朴素检测
        while(s[i-p[i]]==s[i+p[i]])
        {
            p[i]++;
        }
        
        //更新id和mx
        if(i+p[i]>mx)
        {
            mx=i+p[i];
            id=i;
        }
    }
    
    int result=-1;
    for(int i=0;i<p.size();++i)
    {
        result=max(p[i]-1,result);
    }
    return result;
}

int main()
{
    string s;
    while(cin>>s)
       	cout<<manacher(s)<<endl;
    return 0;
}

具有地址alignment的malloc实现

最近看到一个有趣的问题,实现具有机制alignment的malloc函数,在网上查找资料,发现一个有趣的实现方法,在GCC下面成功运行,VS在(**void)寻址出错。

下面是实现代码:

//分配对齐地址的指针
void * aligned_malloc(int size, int alignment)
{
	void* ptr = malloc(size + alignment);

	if (ptr)
	{
		//注意是加上alignment后在取与,与stl中的简单向上取整不一样,stl中的向上取整是加上(alignment-1)
		//这里加上alignment,能确保aligned前面的地址空间存在空白部分
		void* aligned = (void*)(((long)ptr + alignment) & ~(alignment - 1));
		((void**)aligned)[-1] = ptr;//相当于*(aligned-1)的意思,由于语法错误,需要强制转换类型,意思是把ptr存放在aligned-1指向的地址的空间中

		return aligned;
	}
	else
		return NULL;
}
//释放函数aligned_free
void aligned_free(void *paligned)
{
	//删除相应的地址空间
	free(((void**)paligned)[-1]);
}

(hiho编程练习赛1)优化延迟

思路:

1.使用大小为n的堆,进行模拟计算。

2.通过二分法,找出最优的堆的大小。

3.注意q和其他输入的取值大小。

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

小Ho编写了一个处理数据包的程序。程序的输入是一个包含N个数据包的序列。每个数据包根据其重要程度不同,具有不同的”延迟惩罚值”。序列中的第i个数据包的”延迟惩罚值”是Pi。如果N个数据包按照<Pi1, Pi2, … PiN>的顺序被处理,那么总延迟惩罚

SP=1*Pi1+2*Pi2+3*Pi3+…+N*PiN(其中i1, i2, … iN是1, 2, 3, … N的一个排列)。

小Ho的程序会依次处理每一个数据包,这时N个数据包的总延迟惩罚值SP为

1*P1+2*P2+3*P3+…+i*Pi+…+N*PN

小Hi希望可以降低总延迟惩罚值。他的做法是在小Ho的程序中增加一个大小为K的缓冲区。N个数据包在被处理前会依次进入缓冲区。当缓冲区满的时候会将当前缓冲区内”延迟惩罚值”最大的数据包移出缓冲区并进行处理。直到没有新的数据包进入缓冲区时,缓冲区内剩余的数据包会按照”延迟惩罚值”从大到小的顺序被依次移出并进行处理。

例如,当数据包的”延迟惩罚值”依次是<5, 3, 1, 2, 4>,缓冲区大小K=2时,数据包被处理的顺序是:<5, 3, 2, 4, 1>。这时SP=1*5+2*3+3*2+4*4+5*1=38。

现在给定输入的数据包序列,以及一个总延迟惩罚阈值Q。小Hi想知道如果要SP<=Q,缓冲区的大小最小是多少?

输入

Line 1: N Q

Line 2: P1 P2 … PN

对于50%的数据: 1 <= N <= 1000

对于100%的数据: 1 <= N <= 100000, 0 <= Pi <= 1000, 1 <= Q <= 1013

输出

输出最小的正整数K值能满足SP<=Q。如果没有符合条件的K,输出-1。

样例输入
5 38
5 3 1 2 4
样例输出
2

AC代码:


//#include<string>
//#include <iomanip>
#include<vector>
#include <algorithm>
//#include<stack>
#include<set>
#include<queue>
#include<map>
//#include<unordered_set>
#include<unordered_map>
//#include <sstream>
//#include "func.h"
//#include <list>
#include<stdio.h>
#include<iostream>
#include<string>
#include<memory.h>
#include<limits.h>
#include<bitset>
//#include "siukwanAlloctor.h"
using namespace std;

long long arr[100002] = { 0 };
int size = 0;

long long pop()
{
	if (size == 0) return 0;
	long long result = arr[0];//获取栈顶元素
	long long x = arr[--size];//把队尾元素提到0号位置
	arr[size] = 0;
	int i = 0;
	int a = i * 2 + 1;//左孩子
	int b = i * 2 + 2;//右孩子

	while (a<size)
	{
		if (b<size&&arr[a]<arr[b])
			a = b;//右孩子比左孩子大
		if (x<arr[a])//孩子的值比父亲大,提上来
			arr[i] = arr[a];
		else//父亲的值比孩子大,停止操作
			break;
		i = a;
		a = i * 2 + 1;
		b = a + 1;
	}
	arr[i] = x;
	return result;
}
void push(long long x)
{
	int i = size++;
	int p = (i - 1) / 2;//父亲节点
	while (x>arr[p])
	{//孩子节点的值比父亲的值大
		arr[i] = arr[p];
		i = p;
		if (i == 0) break;
		p = (i - 1) / 2;
	}
	arr[i] = x;
}

int main()
{

	int n;
	long long quality;
	int data[100002] = { 0 };
	scanf("%d", &n);
	scanf("%ld", &quality);

	for (int i = 0; i < n; ++i)
	{
		scanf("%d", &data[i]);
	}

	long long  sum = 0;
	int result = -1;
	int a = 1;
	int b = n;
	int mid = (a + b) / 2;
	while (a <= b)
	{
		memset(arr, 0, sizeof(arr));
		sum = 0;
		mid = (a + b) / 2;
		//建立大小为mid的堆
		int i = 0;
		int j = 1;//权重
		for (i = 0; i < mid; ++i)
		{
			push(data[i]);
		}

		//一个一个统计
		for (; i < n; ++i)
		{
			long long x = pop();//缓冲区已经满,把头部弹出来
			sum += x*(j++);
			push(data[i]);
		}
		while (size != 0)
		{
			long long x = pop();//缓冲区已经满,把头部弹出来
			sum += x*(j++);
		}

		if (sum <= quality)
			b = mid - 1;//尝试更小的空间
		else
			a = mid + 1;
		if (sum <= quality && (result == -1 || mid < result))
			result = mid;
	}
	cout << result << endl;

	return 0;
}

(hiho编程练习赛1) 九宫

1.题目要求判断给出一个幻方,只有一种解法则输出解放,否则输出Too Many;

2.采用深度搜索即可,并且每行,每列,每条对角线的和为15,如果填充后任意一个和不等于15,直接进行剪枝。

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

小Hi最近在教邻居家的小朋友小学奥数,而最近正好讲述到了三阶幻方这个部分,三阶幻方指的是将1~9不重复的填入一个3*3的矩阵当中,使得每一行、每一列和每一条对角线的和都是相同的。

三阶幻方又被称作九宫格,在小学奥数里有一句非常有名的口诀:“二四为肩,六八为足,左三右七,戴九履一,五居其中”,通过这样的一句口诀就能够非常完美的构造出一个九宫格来。

有意思的是,所有的三阶幻方,都可以通过这样一个九宫格进行若干镜像和旋转操作之后得到。现在小Hi准备将一个三阶幻方(不一定是上图中的那个)中的一些数组抹掉,交给邻居家的小朋友来进行还原,并且希望她能够判断出究竟是不是只有一组解。

而你呢,也被小Hi交付了同样的任务,但是不同的是,你需要写一个程序~

输入

输入仅包含单组测试数据。

每组测试数据为一个3*3的矩阵,其中为0的部分表示被小Hi抹去的部分。

对于100%的数据,满足给出的矩阵至少能还原出一组可行的三阶幻方。

输出

如果仅能还原出一组可行的三阶幻方,则将其输出,否则输出“Too Many”(不包含引号)。

样例输入
0 7 2
0 5 0
0 3 0
样例输出
6 7 2
1 5 9
8 3 4

AC代码:


//#include<string>
//#include <iomanip>
#include<vector>
#include <algorithm>
//#include<stack>
#include<set>
#include<queue>
#include<map>
//#include<unordered_set>
#include<unordered_map>
//#include <sstream>
//#include "func.h"
//#include <list>
#include<stdio.h>
#include<iostream>
#include<string>
#include<memory.h>
#include<limits.h>
#include<bitset>
//#include "siukwanAlloctor.h"
using namespace std;
bool check(vector<vector<int>>&a)
{
	vector<int> row(3, 0);
	vector<int> col(3, 0);
	vector<int> angle(2, 0);

	//计算行总和
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			if (a[i][j] == 0)
			{
				row[i] = 0;
				break;
			}
			else
				row[i] += a[i][j];
		}
		if (row[i] != 0 && row[i] != 15) return false;
	}

	//计算列总和
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			if (a[j][i] == 0)
			{
				col[i] = 0;
				break;
			}
			else
				col[i] += a[j][i];
		}
		if (col[i] != 0 && col[i] != 15) return false;
	}

	//计算对角线
	if (a[0][0] != 0 && a[1][1] != 0 && a[2][2] != 0)
		angle[0] = a[0][0] + a[1][1] + a[2][2];

	if (angle[0] != 0 && angle[0] != 15) return false;

	if (a[2][0] != 0 && a[1][1] != 0 && a[0][2] != 0)
		angle[1] = a[2][0] + a[1][1] + a[0][2];

	if (angle[1] != 0 && angle[1] != 15) return false;

	return true;

}

void dfs(vector<vector<int>>&a, int need, vector<bool>&used,vector<vector<vector<int>>>&result)
{
	if (result.size() >= 2) return;
	if (need == 0)
	{
		if (result.size()==0)
		result.push_back(a);
		else
		{
			for (int i = 0; i < 3; ++i)
			{
				for (int j = 0; j < 3; ++j)
				{
					if (result[0][i][j] != a[i][j])
					{
						result.push_back(a);
						return;
					}
				}
			}
			return;
		}
	}
	for (int k = 1; k < used.size(); ++k)
	{
		if (!used[k])
		{
			used[k] = true;
			for (int i = 0; i < 3; ++i)
			{
				for (int j = 0; j < 3; ++j)
				{
					if (a[i][j] == 0)//需要填充
					{
						a[i][j] = k;//填充为k
						if (check(a))//检测矩阵
							dfs(a, need - 1, used, result);
						a[i][j] = 0;//填充为k
					}
				}
			}
			used[k] = false;
		}
	}
}


int main()
{
	int need = 0;
	vector<vector<int>> a(3, vector<int>(3, 0));
	vector<bool> used(10,false);
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			scanf("%d", &a[i][j]);
			if (a[i][j] == 0) need++;
			used[a[i][j]] = true;
		}
	}

	vector<vector<vector<int>>> result;
	dfs(a, need, used, result);
	if (result.size()>1)
		cout << "Too Many" << endl;
	else
	{
		for (int i = 0; i < 3; ++i)
		{
			for (int j = 0; j < 3; ++j)
			{
				cout << result[0][i][j];
				if (j != 2)
					cout << " ";
				else
					cout << endl;
			}
		}
	}

	return 0;
}

(hiho)1105 : 题外话·堆

思路:
纯粹是堆的建立和push、pop操作,这次没有使用priority_queue,自己编写了堆。

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

小Ho有一个糖果盒子,每过一段时间小Ho都会将新买来的糖果放进去,同时他也会不断的从其中挑选出最大的糖果出来吃掉,但是寻找最大的糖果不是一件非常简单的事情,所以小Ho希望能够用计算机来他帮忙计算这个问题!

提示:吃糖果吃多了会变胖的!

输入

每个测试点(输入文件)有且仅有一组测试数据。

在一组测试数据中:

第1行为1个整数N,表示需要处理的事件数目。

接下来的M行,每行描述一个事件,且事件类型由该行的第一个字符表示,如果为’A’,表示小Ho将一粒糖果放进了盒子,且接下来为一个整数W,表示这颗糖果的重量;如果为’T’,表示小Ho需要知道当前盒子中最重的糖果的重量是多少,在知道这个值之后,小Ho会将这颗糖果从盒子中取出并吃掉。

对于100%的数据,满足1<=N<=10^5, 1<=w<=10^5。<>

对于100%的数据,满足没有2颗糖果的重量是相同的,最开始的时候小Ho的糖果盒子是空的,且每次小Ho想要取出一颗糖果的时候盒子里一定至少有一颗糖果。

输出

在一组测试数据中:

对于每个类型为’T’的时间,输出1个整数W_MAX,表示在这一时刻,盒子中最重的糖果的重量。

样例输入
5
A 77751
A 1329
A 26239
A 80317
T
样例输出
80317

AC代码:

#include<stdio.h>

int arr[100002] = { 0 };
int size = 0;

int pop()
{
	if (size == 0) return 0;
	int result = arr[0];//获取栈顶元素
	int x = arr[--size];//把队尾元素提到0号位置
	int i = 0;
	int a = i * 2 + 1;//左孩子
	int b = i * 2 + 2;//右孩子

	while (a<size)
	{
		if (b<size&&arr[a]<arr[b])
			a = b;//右孩子比左孩子大
		if (x<arr[a])//孩子的值比父亲大,提上来
			arr[i] = arr[a];
		else//父亲的值比孩子大,停止操作
			break;
		i = a;
		a = i * 2 + 1;
		b = a + 1;
	}
	arr[i] = x;
	return result;
}
void push(int x)
{
	int i = size++;
	int p = (i - 1) / 2;//父亲节点
	while (x>arr[p])
	{//孩子节点的值比父亲的值大
		arr[i] = arr[p];
		i = p;
		if (i == 0) break;
		p = (i - 1) / 2;
	}
	arr[i] = x;
}


int main()
{
	int n;
	scanf("%d", &n);
	while (n--)
	{
		char c[10];
		scanf("%s", c);
		if (*c == 'A')
		{
			int x;
			scanf("%d", &x);
			push(x);
		}
		else
			printf("%d\n", pop());
	}
	return 0;
}