<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>小戴的生活和学习</title>
	<atom:link href="http://www.rockdai.com/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.rockdai.com</link>
	<description>每一份私下的努力都会有加倍的回报</description>
	<lastBuildDate>Thu, 02 Sep 2010 03:13:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>也谈KMP算法</title>
		<link>http://www.rockdai.com/?p=396</link>
		<comments>http://www.rockdai.com/?p=396#comments</comments>
		<pubDate>Thu, 02 Sep 2010 01:43:51 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[KMP算法]]></category>
		<category><![CDATA[字符串]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=396</guid>
		<description><![CDATA[转眼间，下个学期我们专业就要开《数据结构》这门课程了。想当初我自学数据结构的时候可谓是困难重重，现在我想到有可能我身边的同学们也会遇到和我一样的问题。于是我打算尽我的能力，把我在学习算法的过程中的一点感悟，或者是一些我自认为的难点，用我自己的话描述出来。希望可以给朋友们有所帮助。 我记得我遇到的第一个难题就是KMP算法。 KMP算法是一种改进的字符串匹配算法，由Knuth、Morris、Pratt三位前辈提出来的，取了这三个人的名字的头一个字母。KMP算法采用一种预处理，在不匹配发生的时候这个字符串自身包含足够的信息来确定下一个匹配将在哪里开始，以此避免对以前已匹配的字符的重新检查。 首先，我们简单介绍一下KMP算法的主要思路，然后小戴跟大家详细分析一下KMP中预处理待匹配串的过程。 这里我们举一个例子：假设主字符串Text = “abababababababc”，待查找字符串Pattern=” abababc”。我们用两个指针i和j分别表示Text[i – j + 1…i]与Pattern[1…j]匹配。也就是说，i是不断增长的，随着i的增长j相应的变化以达到Text[i]以前的j个字符与Pattern字符串开头的前j个字符相等。当然，j 越大越好了。现在开始匹配Text[i + 1]和Pattern[j + 1]，如果他们相等，那当然最好了，我们继续i++并且j++。如果不匹配的话，我们的策略是减少j的值使得Text[i – j + 1…i]与Pattern[1…j]相等且Text[i + 1]与Pattern[j + 1]也匹配。当j == Pattern串长度的时候，我们就说Pattern串是Text的子串。 我们来看一下KMP算法具体的处理过程： i = 1 2 3 4 5 6 7 8 9 10 &#8230; <a href="http://www.rockdai.com/?p=396">继续阅读 <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>转眼间，下个学期我们专业就要开《数据结构》这门课程了。想当初我自学数据结构的时候可谓是困难重重，现在我想到有可能我身边的同学们也会遇到和我一样的问题。于是我打算尽我的能力，把我在学习算法的过程中的一点感悟，或者是一些我自认为的难点，用我自己的话描述出来。希望可以给朋友们有所帮助。</p>
<p>我记得我遇到的第一个难题就是KMP算法。</p>
<p>KMP算法是一种改进的字符串匹配算法，由Knuth、Morris、Pratt三位前辈提出来的，取了这三个人的名字的头一个字母。KMP算法采用一种预处理，在不匹配发生的时候这个字符串自身包含足够的信息来确定下一个匹配将在哪里开始，以此避免对以前已匹配的字符的重新检查。</p>
<p>首先，我们简单介绍一下KMP算法的主要思路，然后小戴跟大家详细分析一下KMP中预处理待匹配串的过程。</p>
<p>这里我们举一个例子：假设主字符串Text = “abababababababc”，待查找字符串Pattern=” abababc”。我们用两个指针i和j分别表示Text[i – j + 1…i]与Pattern[1…j]匹配。也就是说，i是不断增长的，随着i的增长j相应的变化以达到Text[i]以前的j个字符与Pattern字符串开头的前j个字符相等。当然，j 越大越好了。现在开始匹配Text[i + 1]和Pattern[j + 1]，如果他们相等，那当然最好了，我们继续i++并且j++。如果不匹配的话，我们的策略是减少j的值使得Text[i – j + 1…i]与Pattern[1…j]相等且Text[i + 1]与Pattern[j + 1]也匹配。当j == Pattern串长度的时候，我们就说Pattern串是Text的子串。</p>
<p>我们来看一下KMP算法具体的处理过程：</p>
<pre>i = 1 2 3 4 5 <span style="color: #ff0000;">6</span> 7 8 9 10 11 12 13 14 15
T = a b a b a <span style="color: #ff0000;">b</span> a b a b  a  b  a  b  c
P = a b a b a <span style="color: #ff0000;">b</span> c
j = 1 2 3 4 5 <span style="color: #ff0000;">6</span> 7</pre>
<p>当i = j = 6时，Text[7] != Pattern[7]，那么我们就要减小j以使得串重新匹配。仔细观察一下我们发现，如果我们要避免重复比较之前已经匹配的字符串，我们需要找到一个最大的j，使得Pattern串的前j个字符与Text[6]之前的j个字符相等。而我们能匹配到这一步，就必定已经有Pattern[6]之前的字符与Text[6]之前的字符相等。换句话说，我们就是要找到一个最大的j，使得Pattern[1…6]的前j个字符与后j个字符相等。</p>
<p>在这里，Pattern[1…4] = “abab”，Pattern[3…6] = “abab”，于是，新的j = 4：</p>
<pre>i = 1 2 3 4 5 <span style="color: #ff0000;">6</span> 7 8 9 10 11 12 13 14 15
T = a b a b a <span style="color: #ff0000;">b</span> a b a b  a  b  a  b  c
P =     a b a <span style="color: #ff0000;">b</span> a b c
j =     1 2 3 <span style="color: #ff0000;">4</span> 5 6 7</pre>
<p>以此类推，当匹配到i = 9, j = 7的时候，我们将会需要再一次地改变j。</p>
<p>在我们刚才的讨论中，j在每次失去匹配的时候需要作如何处理其实跟Text串无关，我们仅仅通过考虑Pattern串就能得出。这就是之前我们说的预处理，通过预处理我们可以得到一个数组，其实也就是严蔚敏老奶奶的《数据结构》里说的Next函数。当我们匹配到j，而j + 1不能继续匹配时，这时新的j应该是所有满足Pattern[1…next[j]] == Pattern[next[j]…j]的最大值。</p>
<p>下面是KMP算法的主过程：</p>
<pre class="brush: cpp;">
void kmpMatch() {
	int j = -1;
	for(int i = 0; i &lt; TEXT_LENGTH; i++) {
		while(j &gt; -1 &amp;&amp; text[i] != pattern[j + 1]) {
			j = next[j];
		}
		if(text[i] == pattern[j + 1]) {
			j++;
		}
		if(j == (PATTERN_LENGTH - 1)) {
			printf(&quot;Pattern occurs with shift %d\n&quot;, i - (PATTERN_LENGTH - 1));
			j = next[j];
		}
	}
}
</pre>
<p>那么，如果快速而有效地进行这个预处理呢？我个人认为这是理解KMP算法的一大难点。几乎所有的资料或者教科书上都会说求得next数组的过程其实就是对Pattern串的一次自我匹配，然后说由于代码和KMP算法主过程代码类似，于是就一笔带过了。</p>
<p>可是很惭愧啊，我当时学习的时候偏偏就是不能理解这个过程。现在终于对它有一点儿明白了，在这里我把我的理解说出来。</p>
<p>我尽量给它阐述清楚，如有不对的地方欢迎大家拍砖。</p>
<pre>     -1 0  1 2 3 4 5  6
P =     a  b a b a b  c
next = -1 -1 0 1 2 3 -1</pre>
<p>我们先来观察一下。为了下面表述方便，我们把Pattern串起始的0位的前一位，也就是我们标记出来的-1位显式地表示出来。</p>
<p>我们在这里仍然虚拟出两个指针i和j。其中i是持续增长的，用来遍历整个Pattern串，而j则标识当前Pattern[i – j + 1…i]与Pattern[1…j]能匹配的最长串。</p>
<p>我们从左边开始，当Pattern串连第一个元素”a”，即Pattern[0]，都不与Text串匹配时，我们别无它法，只能将整个Pattern串右移一位后再和Text串匹配，所以我们令next[0] = -1。此时j也应该被初始化为-1。</p>
<p>我们继续向前，检查Pattern[1] = b。因为它不等于第一个元素”a”，亦即Pattern[1] != Pattern[next[0] + 1]，所以要将j向后退，得到next[1] = -1。</p>
<p>到了Pattern[2] = a时，因为有Pattern[2] == Pattern[0]，于是j不用退，而是增加1。next[2] = 0。</p>
<p>同样，到了Pattern[3]时，由Pattern[3] == Pattern[1]，next[3] = 1。</p>
<p>以此类推，得到next[4] = 2，next[5] = 3。</p>
<p>好了，当我们考虑Pattern[6]时，我们发现Pattern[6] != Pattern[next[5] + 1]，再一次考虑j向后退。此时的j等于3。而前面已经算得next[3] = 1，于是我们令j倒退到1，即从next[5]倒退到next[3]。</p>
<p>然而Pattern[6]居然仍然不等于Pattern[j + 1]，我们只好把j继续向后退到next[j]即next[1]，这样一来j的值又变为-1。</p>
<p>很明显的，Pattern[6] != Pattern[next[1] + 1]。于是j不会再往后面退了，最终，我们得到next[6] = -1。</p>
<p>下面给出预处理过程的代码：</p>
<pre class="brush: cpp;">
void getNext() {
	int j = -1;
	next[0] = -1;
	for(int i = 1; i &lt; PATTERN_LENGTH; i++) {
		while(j &gt; -1 &amp;&amp; pattern[j + 1] != pattern[i]) {
			j = next[j];
		}
		if(pattern[j + 1] == pattern[i]) {
			j++;
		}
		next[i] = j;
	}
}
</pre>
<p>KMP算法是一个很有研究价值的问题，它可以在线性时间内解决字符串的匹配，我听说正则表达式的解析器在解决字符串匹配的过程中也是用的KMP算法。</p>
<p>当然，我们解决字符串匹配还有很多其他的方法，比如自动机等。以后我们有可能会提到。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=396</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>腾讯的一道面试题（截取字符串）</title>
		<link>http://www.rockdai.com/?p=387</link>
		<comments>http://www.rockdai.com/?p=387#comments</comments>
		<pubDate>Mon, 30 Aug 2010 08:43:57 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[腾讯]]></category>
		<category><![CDATA[面试题]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=387</guid>
		<description><![CDATA[题目如下： 假设有“123&#60;em&#62;abc&#60;/em&#62;456&#60;em&#62;def&#60;/em&#62;789”这么一个字符串，写一个函数，可以传入一个字符串，和一个要截取的长度。返回截取后的结果。 要求： 1、&#60;em&#62;和&#60;/em&#62;标记不得计算在长度之内。 2、截取后的字符串，要保留原有&#60;em&#62;标签，不过如果最后有一个标签没有闭合，则去掉其开始标签。 示例： 题中的字符串，要截取长度5，则返回的字符串应该为：123ab；要截取长度8，应返回123&#60;em&#62;abc&#60;/em&#62;45。 看到题目后，脑海中首先想到的就是用栈来解决。 代码写得很快，实现起来很简单。但是仔细琢磨一下的话，很容易发现还是有很多问题的。 比如对于一些不需要关闭的标签（如：&#60;br /&#62;），这段代码会出现问题。 我先把今天写的代码贴出来，等清闲了再容我仔细推敲一下。 function getSubstr($str, $length) { $result = ''; $tag = ''; $stack = array(); for($i = 0; $i &#60; $length; $i++) { $result .= $str[$i]; if ($str[$i] == '&#60;') &#8230; <a href="http://www.rockdai.com/?p=387">继续阅读 <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>题目如下：<br />
假设有“123&lt;em&gt;abc&lt;/em&gt;456&lt;em&gt;def&lt;/em&gt;789”这么一个字符串，写一个函数，可以传入一个字符串，和一个要截取的长度。返回截取后的结果。<br />
要求：<br />
1、&lt;em&gt;和&lt;/em&gt;标记不得计算在长度之内。<br />
2、截取后的字符串，要保留原有&lt;em&gt;标签，不过如果最后有一个标签没有闭合，则去掉其开始标签。<br />
示例：<br />
题中的字符串，要截取长度5，则返回的字符串应该为：123ab；要截取长度8，应返回123&lt;em&gt;abc&lt;/em&gt;45。</p>
<p>看到题目后，脑海中首先想到的就是用栈来解决。<br />
代码写得很快，实现起来很简单。但是仔细琢磨一下的话，很容易发现还是有很多问题的。 比如对于一些不需要关闭的标签（如：&lt;br /&gt;），这段代码会出现问题。<br />
我先把今天写的代码贴出来，等清闲了再容我仔细推敲一下。</p>
<pre class="brush: php;">
function getSubstr($str, $length) {

	$result = '';
	$tag = '';
	$stack = array();

	for($i = 0; $i &lt; $length; $i++) {

		$result .= $str[$i];

		if ($str[$i] == '&lt;') {	// 是 HTML 标签

			// 获取完整的HTML标签，保存到$tag
			for ($j = $i; $str[$j] != '&gt;'; $j++, $length++) {
				$tag .= $str[$j];
			}
			$length++;
			$tag .= $str[$j];

			// 匹配
			if(preg_match('/&lt;([^\/]+)?&gt;/i', $tag, $tmp)) { // 如果是开始标签，则入栈
				array_push($stack, $tmp[1]);
			} else if(preg_match('/' . $stack[count($stack) - 1] . '/', $tag)) { // 如果是结束标签，则出栈
				array_pop($stack);
			}

			$tag = '';
		}
	}

	if (empty($stack)) { // 如果栈为空，则此时 $result 即为要求的串
		return $result;
	}

	while (!empty($stack)) { // 如果栈不为空则清除没有被关闭的开始标签

		$tag = array_pop($stack);
		$pos = strrpos($result, $tag);

		for ($i = $pos - 1; $result[$i] != '&gt;'; $i++) {
			$result[$i] = '';
		}
		$result[$i++] = '';
	}

	return $result;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=387</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>2010网易编程挑战赛：最大和子序列</title>
		<link>http://www.rockdai.com/?p=372</link>
		<comments>http://www.rockdai.com/?p=372#comments</comments>
		<pubDate>Fri, 06 Aug 2010 03:43:20 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[动态规划]]></category>
		<category><![CDATA[有道]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[网易]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=372</guid>
		<description><![CDATA[原题地址：http://poj.youdao.com/nanti1/C/ 两个dp函数。 #include &#60;cstdio&#62; #include &#60;iostream&#62; using namespace std; #define INF 1000000000 int a[50000], sum[50000]; int n; void init() { scanf(&#34;%d&#34;,&#38;n); for(int i = 0; i &#60; n; i++) { scanf(&#34;%d&#34;, &#38;a[i]); sum[i] = (i ? sum[i - 1] + &#8230; <a href="http://www.rockdai.com/?p=372">继续阅读 <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>原题地址：<a href="http://poj.youdao.com/nanti1/C/" target="_blank">http://poj.youdao.com/nanti1/C/</a></p>
<p>两个dp函数。</p>
<pre class="brush: cpp;">
#include &lt;cstdio&gt;
#include &lt;iostream&gt;

using namespace std;

#define INF 1000000000
int a[50000], sum[50000];
int n;

void init() {
	scanf(&quot;%d&quot;,&amp;n);
	for(int i = 0; i &lt; n; i++) {
		scanf(&quot;%d&quot;, &amp;a[i]);
		sum[i] = (i ? sum[i - 1] + a[i] : a[i]);
	}
} // init

int threeSegments() {
	if(n &lt; 3)
		return -INF;
	int f1[50000][2];
	// f1[i][i是否是最后一段的末尾,1:是,0:无限制]
	for(int i = 0; i &lt; n; i++) {

		if(i == 0) {
			f1[i][1] = a[i];
			f1[i][0] = a[i];
		} else {
			f1[i][1] = f1[i - 1][1] + a[i];
			f1[i][0] = max( max(f1[i - 1][1], f1[i - 1][0]), f1[i][1] );
		} // i IF
	} // i FOR

	int f2[50000][2];
	for(int i = 1; i &lt; n; i++) {
		if(i == 1) {
			f2[i][1] = a[0] + a[1];
			f2[i][0] = a[0] + a[1];
		} else {
			f2[i][1] = max(f2[i - 1][1] + a[i], f1[i - 1][0] + a[i]);
			f2[i][0] = max(f2[i - 1][0], f2[i][1]);
		} // i IF
	} // i FOR

	int f3[50000];
	int rt = -INF;
	for(int i = 2; i &lt; n; i++) {
		f3[i] = f2[i - 1][0] + sum[n - 1] - sum[i - 1];
		if(rt &lt; f3[i]) {
			rt = f3[i];
		} // rt IF
	} // i FOR
	return rt;
} // threesegments

int twoSegments() {

	int f1[50000][2];
	for(int i = 0; i &lt; n; i++) {
		if(i) {

			f1[i][1] = max(f1[i - 1][1] + a[i], a[i]);
			f1[i][0] = max(f1[i - 1][0], f1[i][1]);
        } else {

			f1[i][1] = a[i];
			f1[i][0] = a[i];
		} // i IF
	} // i FOR

	int f2[50000][2];
	for(int i = 1; i &lt; n; i++) {
		if(i == 1) {

			f2[i][1] = a[0] + a[1];
			f2[i][0] = a[0] + a[1];
		} else {

			f2[i][1] = max(f2[i - 1][1] + a[i], f1[i - 1][0] + a[i]);
			f2[i][0] = max(f2[i - 1][0], f2[i][1]);
        } // i IF
    } // i FOR

    return f2[n - 1][0];
} // twosegments

int main() {
	int t;
	cin &gt;&gt; t;
	while(t--) {
		init();
		cout &lt;&lt; max(threeSegments(), twoSegments()) &lt;&lt; &quot;\n&quot;;
	} // t WHILE
	return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=372</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>棋盘覆盖问题的递归实现</title>
		<link>http://www.rockdai.com/?p=367</link>
		<comments>http://www.rockdai.com/?p=367#comments</comments>
		<pubDate>Fri, 06 Aug 2010 02:07:18 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[分治法]]></category>
		<category><![CDATA[棋盘覆盖]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[递归]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=367</guid>
		<description><![CDATA[问题描述 在一个2^k x 2^k个方格组成的棋盘中，若恰有一个方格与其他方格不同，则称该方格为一特殊方格，且称该棋盘为一特殊棋盘。显然特殊方格在棋盘上出现的位置有4^k种情形。要用下图所示的4种不同形态的L型骨牌覆盖一个给定的特殊棋盘上除特殊方格外的所有方格，且任何2个L型骨牌不得重叠覆盖。易知，在任何一个2^k x 2^k的棋盘覆盖中，用到的L型骨牌个数恰为(4^k-1)/3。 算法分析 用递归的方法（也称分治法）可以设计解棋盘覆盖问题的一个简捷算法。当k&#62;0时，将2^k x 2^k棋盘分割为4个2^(k-1) x 2^(k-1)子棋盘。特殊方格必位于4个较小子棋盘之一中，其余3个子棋盘中无特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘，可以用一个L型骨牌覆盖这3个较小棋盘的会和处，则这3个子棋盘上被L型骨牌覆盖的方格就成为该棋盘上的特殊方格，从而将原问题转化为4个较小规模的棋盘覆盖问题。递归地使用这种分割，直至棋盘简化为1&#215;1棋盘。 算法复杂度分析 设T(k)是算法覆盖一个2^k x 2^k棋盘所需的时间，则从算法的分治策略可知，T(k)满足如下递归关系：当k=0时，T(k)=O(1)；当k&#62;0时，T(k)=4T(k-1)+O(1)。解此递归关系得T(k)=O(4^k)。由于覆盖一个2^k x 2^k棋盘所需的L型骨牌个数为(4^k-1)/3，故该算法是一个在渐近意义下最优的算法。 #include &#60;iostream&#62; using namespace std; int tile = 1; // L型骨牌的编号(递增) int board[100][100]; // 棋盘 /***************************************************** * 递归方式实现棋盘覆盖算法 * 输入参数： * currentRow：当前棋盘左上角的行号 &#8230; <a href="http://www.rockdai.com/?p=367">继续阅读 <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><strong>问题描述</strong><br />
在一个2^k x 2^k个方格组成的棋盘中，若恰有一个方格与其他方格不同，则称该方格为一特殊方格，且称该棋盘为一特殊棋盘。显然特殊方格在棋盘上出现的位置有4^k种情形。要用下图所示的4种不同形态的L型骨牌覆盖一个给定的特殊棋盘上除特殊方格外的所有方格，且任何2个L型骨牌不得重叠覆盖。易知，在任何一个2^k x 2^k的棋盘覆盖中，用到的L型骨牌个数恰为(4^k-1)/3。</p>
<p><a href="http://www.rockdai.com/wp-content/uploads/2010/08/chessBoard.jpg"><img class="alignnone size-medium wp-image-368" title="棋盘覆盖问题的递归实现" src="http://www.rockdai.com/wp-content/uploads/2010/08/chessBoard-300x64.jpg" alt="棋盘覆盖问题的递归实现" width="300" height="64" /></a></p>
<p><strong>算法分析</strong><br />
用递归的方法（也称分治法）可以设计解棋盘覆盖问题的一个简捷算法。当k&gt;0时，将2^k x 2^k棋盘分割为4个2^(k-1)  x 2^(k-1)子棋盘。特殊方格必位于4个较小子棋盘之一中，其余3个子棋盘中无特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘，可以用一个L型骨牌覆盖这3个较小棋盘的会和处，则这3个子棋盘上被L型骨牌覆盖的方格就成为该棋盘上的特殊方格，从而将原问题转化为4个较小规模的棋盘覆盖问题。递归地使用这种分割，直至棋盘简化为1&#215;1棋盘。</p>
<p><strong>算法复杂度分析</strong><br />
设T(k)是算法覆盖一个2^k x 2^k棋盘所需的时间，则从算法的分治策略可知，T(k)满足如下递归关系：当k=0时，T(k)=O(1)；当k&gt;0时，T(k)=4T(k-1)+O(1)。解此递归关系得T(k)=O(4^k)。由于覆盖一个2^k x 2^k棋盘所需的L型骨牌个数为(4^k-1)/3，故该算法是一个在渐近意义下最优的算法。</p>
<pre class="brush: cpp;">
#include &lt;iostream&gt;
using namespace std;

int tile = 1;	// L型骨牌的编号(递增)
int board[100][100];	// 棋盘 

/*****************************************************
 *	递归方式实现棋盘覆盖算法
 *	输入参数：
 *	currentRow：当前棋盘左上角的行号
 *	currentCol：当前棋盘左上角的列号
 *	targetRow：当前特殊方格所在的行号
 *	targetCol：当前特殊方格所在的列号
 *	size：当前棋盘的：2^k
 *****************************************************/
void chessBoard(int currentRow, int currentCol, int targetRow, int targetCol, int size) {

	if(size == 1)	// 棋盘方格大小为1，说明递归到最里层
		return;
	int t = tile++;	// 每次递增1
	int s = size / 2;	// 棋盘中间的行、列号(相等的)

	//检查特殊方块是否在左上角子棋盘中
	if(targetRow &lt; currentRow + s &amp;&amp; targetCol &lt; currentCol + s) {	// 在

		chessBoard(currentRow, currentCol, targetRow, targetCol, s);

	} else { // 不在，将该子棋盘右下角的方块视为特殊方块

		board[currentRow + s - 1][currentCol + s - 1] = t;
		chessBoard(currentRow, currentCol, currentRow+s-1, currentCol+s-1, s);
	}
	// 检查特殊方块是否在右上角子棋盘中
	if(targetRow &lt; currentRow + s &amp;&amp; targetCol &gt;= currentCol + s) {	// 在

		chessBoard(currentRow, currentCol + s, targetRow, targetCol, s);

	} else {	// 不在，将该子棋盘左下角的方块视为特殊方块

		board[currentRow + s - 1][currentCol + s] = t;
		chessBoard(currentRow, currentCol+s, currentRow+s-1, currentCol+s, s);
	}
	//检查特殊方块是否在左下角子棋盘中
	if(targetRow &gt;= currentRow + s &amp;&amp; targetCol &lt; currentCol + s) {	// 在

		chessBoard(currentRow + s, currentCol, targetRow, targetCol, s);

	} else {	// 不在，将该子棋盘右上角的方块视为特殊方块

		board[currentRow + s][currentCol + s - 1] = t;
		chessBoard(currentRow+s, currentCol, currentRow+s, currentCol+s-1, s);
	}
	//检查特殊方块是否在右下角子棋盘中
	if(targetRow &gt;= currentRow + s &amp;&amp; targetCol &gt;= currentCol + s) {	// 在

		chessBoard(currentRow + s, currentCol + s, targetRow, targetCol, s);

	} else {	// 不在，将该子棋盘左上角的方块视为特殊方块

		board[currentRow + s][currentCol + s] = t;
		chessBoard(currentRow+s, currentCol+s, currentRow+s, currentCol+s, s);
	}
}

int main() {

	int size;
	cout &lt;&lt; &quot;输入棋盘的 size （大小必须是2的n次幂）：&quot;;
	cin &gt;&gt; size;
	int indexX, indexY;
	cout &lt;&lt; &quot;输入特殊方格位置的坐标: &quot;;
	cin &gt;&gt; indexX &gt;&gt; indexY;
	chessBoard(0, 0, indexX, indexY, size);

	for(int i = 0; i &lt; size; i++) {
		for(int j = 0; j &lt; size; j++) {
			cout &lt;&lt; board[i][j] &lt;&lt; &quot;\t&quot;;
		}
		cout &lt;&lt; endl;
	}
	return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=367</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>2010网易编程挑战赛：有“道”难题</title>
		<link>http://www.rockdai.com/?p=350</link>
		<comments>http://www.rockdai.com/?p=350#comments</comments>
		<pubDate>Tue, 27 Jul 2010 11:30:55 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[ACM/ICPC]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[有道]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[网易]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=350</guid>
		<description><![CDATA[原题地址：http://poj.youdao.com/nanti2/A/ Description ‘道’是中国古代哲学的重要范畴。用以说明世界的本原、本体、规律或原理。在不同的哲学体系中，其涵义有所不同。老子所写的《道德经》是关于‘道’的经典著作。 道的原始涵义指道路、坦途，以后逐渐发展为道理，用以表达事物的规律性。这一变化经历了相当长的历史过程。《易经》中有“复自道，何其咎”（《小畜》），“履道坦坦”（《履》），“反复其道，七日来复”(《复》)，都为道路之义。 《尚书•洪范》中说：“无有作好,遵王之道；无有作恶,遵王之路。无偏无党,王道荡荡;无党无偏,王道平平；无反无侧,王道正直”。这里的道，已经有正确的政令、规范和法度的意思,说明“道”的概念已向抽象化发展。 &#8212;- 节选自有道词典(http://dict.youdao.com) Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一。它把每三个8Bit的字节转换为四个6Bit的字节（3*8 = 4*6 = 24），然后把6Bit再添两位高位0，组成四个8Bit的字节，也就是说，转换后的字符串理论上将要比原来的长1/3。 有道的工程师闲暇之余，将Base64编码改成了Base4编码，即把每个8Bit的字节转换成4个2Bit的字节，然后以4个字符来代替。编码和字符的对应方案如下： 00 &#8212;- a 01 &#8212;- o 10 &#8212;- d 11 &#8212;- 空格 这样，编码后的字符串就只会含有字符‘d&#8217;,'a&#8217;,'o&#8217;和空格。 例如字符y ,其ASCII码是121，对应的二进制串是01111001，这样分成 01 11 10 01四块后，用Base4编码后的字符串为&#8221;o do&#8221;。 有道的工程师很好奇，按照这种编码方式，编码后的字符串中含有多少个完整的dao呢？一个完整的dao由连续的‘d&#8217;,'a&#8217;,'o&#8217;三个字符组成。 Input 第一行有一个正整数n，代表接下来有n个待编码的原串。（1 &#60;= n &#60;= 10) &#8230; <a href="http://www.rockdai.com/?p=350">继续阅读 <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>原题地址：<a href="http://poj.youdao.com/nanti2/A/" target="_blank">http://poj.youdao.com/nanti2/A/</a></p>
<p><strong>Description<br />
</strong>‘道’是中国古代哲学的重要范畴。用以说明世界的本原、本体、规律或原理。在不同的哲学体系中，其涵义有所不同。老子所写的《道德经》是关于‘道’的经典著作。</p>
<p>道的原始涵义指道路、坦途，以后逐渐发展为道理，用以表达事物的规律性。这一变化经历了相当长的历史过程。《易经》中有“复自道，何其咎”（《小畜》），“履道坦坦”（《履》），“反复其道，七日来复”(《复》)，都为道路之义。</p>
<p>《尚书•洪范》中说：“无有作好,遵王之道；无有作恶,遵王之路。无偏无党,王道荡荡;无党无偏,王道平平；无反无侧,王道正直”。这里的道，已经有正确的政令、规范和法度的意思,说明“道”的概念已向抽象化发展。</p>
<p>&#8212;- 节选自有道词典(http://dict.youdao.com)</p>
<p>Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一。它把每三个8Bit的字节转换为四个6Bit的字节（3*8 = 4*6 = 24），然后把6Bit再添两位高位0，组成四个8Bit的字节，也就是说，转换后的字符串理论上将要比原来的长1/3。</p>
<p>有道的工程师闲暇之余，将Base64编码改成了Base4编码，即把每个8Bit的字节转换成4个2Bit的字节，然后以4个字符来代替。编码和字符的对应方案如下：<br />
00 &#8212;- a<br />
01 &#8212;- o<br />
10 &#8212;- d<br />
11 &#8212;- 空格<br />
这样，编码后的字符串就只会含有字符‘d&#8217;,'a&#8217;,'o&#8217;和空格。</p>
<p>例如字符y ,其ASCII码是121，对应的二进制串是01111001，这样分成 01 11 10 01四块后，用Base4编码后的字符串为&#8221;o do&#8221;。</p>
<p>有道的工程师很好奇，按照这种编码方式，编码后的字符串中含有多少个完整的dao呢？一个完整的dao由连续的‘d&#8217;,'a&#8217;,'o&#8217;三个字符组成。</p>
<p><strong>Input<br />
</strong>第一行有一个正整数n，代表接下来有n个待编码的原串。（1 &lt;= n &lt;= 10)<br />
接下来有n行，每行有一个长度不超过106的原串，原串中的字符可能为ASCII码中除换行符以外的任意可见字符。</p>
<p><strong>Output<br />
</strong>共有n行，每行为一个整数k, 表示输入数据中对应的原串用Base4编码后含有多少个完整的dao。</p>
<p><strong>Sample Input<br />
</strong>2<br />
www.youdao.com<br />
dict.youdao.com</p>
<p><strong>Sample Output<br />
</strong>1<br />
1</p>
<p><strong>Hint<br />
</strong>Java时限是标准时限的3倍，而且对于每个输入文件都有100ms的额外IO时间</p>
<p><span id="more-350"></span></p>
<pre class="brush: cpp;">
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;math.h&gt;
#include &lt;algorithm&gt;
#include &lt;string.h&gt;
#include &lt;fstream&gt;

using namespace std;

int main() {

	//ifstream cin(&quot;d:\\test.txt&quot;);
	int n;
	cin &gt;&gt; n;
	while(n--) {

		string s;
		cin &gt;&gt; s;
		int num = 0;
		int stat = 0;
		int r = 0;
		for(long i = 0; i &lt; s.length(); i++) {

			int c[4];
			int t = (int)s[i];
			c[0] = t &gt;&gt; 6;
			t %= 64;
			c[1] = t &gt;&gt; 4;
			t %= 16;
			c[2] = t &gt;&gt; 2;
			c[3] = t % 4;

			for(int j = 0; j &lt; 4; j++) {

				int ch = c[j];
				if (ch == 2)
					stat = 1;
				else if (ch == 0 &amp;&amp; stat == 1)
					stat = 2;
				else if (ch == 1 &amp;&amp; stat == 2)
					stat = 3;
				else
					stat = 0;
				if (stat == 3) {
					r++;
				} // if
			} // for
		} // for

		cout&lt;&lt;r&lt;&lt;endl;
	} // while
	return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=350</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>2010网易编程挑战赛：有道搜索框</title>
		<link>http://www.rockdai.com/?p=345</link>
		<comments>http://www.rockdai.com/?p=345#comments</comments>
		<pubDate>Sun, 25 Jul 2010 07:23:55 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[ACM/ICPC]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[有道]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[网易]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=345</guid>
		<description><![CDATA[原题地址：http://poj.youdao.com/nanti1/B/ Description 在有道搜索框中，当输入一个或者多个字符时，搜索框会出现一定数量的提示，如下图所示： 现在给你N个单词和一些查询，请输出提示结果，为了简化这个问题，只需要输出以查询词为前缀的并且按字典序排列的最前面的8个单词，如果符合要求的单词一个也没有请只输出当前查询词。 Input 第一行是一个正整数N，表示词表中有N个单词。 接下来有N行，每行都有一个单词，注意词表中的单词可能有重复，请忽略掉重复单词。所有的单词都由小写字母组成。 接下来的一行有一个正整数Q，表示接下来有Q个查询。 接下来Q行，每行有一个单词，表示一个查询词，所有的查询词也都是由小写字母组成，并且所有的单词以及查询的长度都不超过20，且都不为空 其中：N&#60;=10000,Q&#60;=10000 Output 对于每个查询，输出一行，按顺序输出该查询词的提示结果，用空格隔开。 Sample Input 10 a ab hello that those dict youdao world your dictionary 6 bob d dict dicti yo z Sample Output bob dict dictionary dict dictionary dictionary &#8230; <a href="http://www.rockdai.com/?p=345">继续阅读 <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>原题地址：<a href="http://poj.youdao.com/nanti1/B/" target="_blank">http://poj.youdao.com/nanti1/B/</a></p>
<p><strong>Description</strong><br />
在有道搜索框中，当输入一个或者多个字符时，搜索框会出现一定数量的提示，如下图所示：<br />
<img class="alignnone" title="有道搜索框" src="http://poj.youdao.com/groups/youdao/suggest.jpg" alt="" width="567" height="442" /><br />
现在给你N个单词和一些查询，请输出提示结果，为了简化这个问题，只需要输出以查询词为前缀的并且按字典序排列的最前面的8个单词，如果符合要求的单词一个也没有请只输出当前查询词。</p>
<p><strong>Input</strong><br />
第一行是一个正整数N，表示词表中有N个单词。<br />
接下来有N行，每行都有一个单词，注意词表中的单词可能有重复，请忽略掉重复单词。所有的单词都由小写字母组成。<br />
接下来的一行有一个正整数Q，表示接下来有Q个查询。<br />
接下来Q行，每行有一个单词，表示一个查询词，所有的查询词也都是由小写字母组成，并且所有的单词以及查询的长度都不超过20，且都不为空<br />
其中：N&lt;=10000,Q&lt;=10000</p>
<p><strong>Output</strong><br />
对于每个查询，输出一行，按顺序输出该查询词的提示结果，用空格隔开。</p>
<p><strong>Sample Input<br />
</strong>10<br />
a<br />
ab<br />
hello<br />
that<br />
those<br />
dict<br />
youdao<br />
world<br />
your<br />
dictionary<br />
6<br />
bob<br />
d<br />
dict<br />
dicti<br />
yo<br />
z</p>
<p><strong>Sample Output</strong><br />
bob<br />
dict dictionary<br />
dict dictionary<br />
dictionary<br />
youdao your<br />
z</p>
<p><span id="more-345"></span></p>
<pre class="brush: cpp;">
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
#include &lt;algorithm&gt;

using namespace std;

bool cmp(const string dic, const string in) {

	if(dic.length() &lt; in.length())
		return false;

	return in == dic.substr(0, in.length());
}

int main() {
	//ifstream cin(&quot;test.txt&quot;);
	int n, q;
	vector&lt;string&gt; dict;
	string s;
	cin&gt;&gt;n;
	dict.reserve(n);
	for(int i = 0; i &lt; n; i++) {

		cin&gt;&gt;s;
		dict.push_back(s);
	}
	std::sort(dict.begin(), dict.end());
	dict.erase(std::unique(dict.begin(), dict.end()), dict.end());
	cin&gt;&gt;q;
	for(int i = 0; i &lt; q; i++) {

		string s;
		cin&gt;&gt;s;

		int t = 0;
		for(int j = 0; j &lt; dict.size(); j++) {

			if(cmp(dict[j], s)) {

				cout&lt;&lt;dict[j]&lt;&lt;&quot; &quot;;
				t = 1;
			}
		}
		if(t == 0)
			cout&lt;&lt;s;
		cout&lt;&lt;endl;
	}

	return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=345</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>2010网易编程挑战赛：另类的异或</title>
		<link>http://www.rockdai.com/?p=336</link>
		<comments>http://www.rockdai.com/?p=336#comments</comments>
		<pubDate>Thu, 22 Jul 2010 09:15:36 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[ACM/ICPC]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[有道]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[网易]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=336</guid>
		<description><![CDATA[原题地址：http://poj.youdao.com/nanti1/A/ Description 对于普通的异或，其实是二进制的无进位的加法 这里我们定义一种另类的异或A op B, op是一个仅由^组成的字符串，如果op中包含n个^，那么A op B表示A和B之间进行n+1进制的无进位的加法。 下图展示了3 ^ 5 和 4 ^^ 5的计算过程 Input 第一行有一个正整数T, 表示下面共有T组测试数据。 接下来T行，每行有一组测试数据，是由空格隔开的三个部分组成： A B C A和C是两个十进制整数，B是一个字符串，由n个^组成 1 &#60;= T &#60;= 100, 0&#60;=A,B&#60;2^30, 1&#60;=n&#60;=1000 Output 每个测试数据输出一行，包含一个数字，即该数据的结果，用十进制表示。 Sample Input 2 3 ^ 5 4 &#8230; <a href="http://www.rockdai.com/?p=336">继续阅读 <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>原题地址：<a href="http://poj.youdao.com/nanti1/A/" target="_blank">http://poj.youdao.com/nanti1/A/</a></p>
<p><strong>Description</strong><br />
对于普通的异或，其实是二进制的无进位的加法<br />
这里我们定义一种另类的异或A op B, op是一个仅由^组成的字符串，如果op中包含n个^，那么A op B表示A和B之间进行n+1进制的无进位的加法。<br />
下图展示了3 ^ 5 和 4 ^^ 5的计算过程<br />
<img class="alignnone" title="另类的异或" src="http://poj.youdao.com/groups/youdao/xor.jpg" alt="另类的异或" width="251" height="229" /></p>
<p><strong>Input</strong><br />
第一行有一个正整数T, 表示下面共有T组测试数据。<br />
接下来T行，每行有一组测试数据，是由空格隔开的三个部分组成：<br />
A B C<br />
A和C是两个十进制整数，B是一个字符串，由n个^组成<br />
1 &lt;= T &lt;= 100, 0&lt;=A,B&lt;2^30, 1&lt;=n&lt;=1000</p>
<p><strong>Output</strong><br />
每个测试数据输出一行，包含一个数字，即该数据的结果，用十进制表示。</p>
<p><strong>Sample Input</strong><br />
2<br />
3 ^ 5<br />
4 ^^ 5</p>
<p><strong>Sample Output</strong><br />
6<br />
6</p>
<p><span id="more-336"></span></p>
<pre class="brush: cpp;">
#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;math.h&gt;
#include &lt;string.h&gt;
#include &lt;fstream&gt;

using namespace std;

int num[100], num2[100], re[100];
int r1, r2, x, i, temp, maxLen;
int result;

void process() {

	i = 0;
	while ((int) (r1 / x) &gt; 0) {

		num[i++] = r1 % x;
		r1 = r1 / x;
	}
    num[i] = r1;
    maxLen = i;

    i = 0;
    while ((int) (r2 / x) &gt; 0) {

        num2[i++] = r2 % x;
        r2 = r2 / x;
    }
    num2[i] = r2;
    maxLen = maxLen &gt; i ? maxLen : i;

    i = 0;
    result = 0;
    for (temp = maxLen; temp &gt;= 0; temp--) {

		re[i] = (num[temp] + num2[temp]) % x;
		result += re[i] * ( (int)pow( (float)x, temp ) );
		i++;
    }
    cout &lt;&lt; result &lt;&lt; endl;
}

void init() {

	memset(num, 0, sizeof (num));
	memset(num2, 0, sizeof (num2));
	memset(re, 0, sizeof (re));
	result = 0;
}

int main() {
	//ifstream cin(&quot;test.txt&quot;);
	int n;
	cin &gt;&gt; n;
	while (n--) {
		init();
		cin &gt;&gt; r1;
		string op;
		cin &gt;&gt; op;
		int length = op.length();
		cin &gt;&gt; r2;

		x = ++length;
		process();
	}
	return 0;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=336</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>快速上手监控宝</title>
		<link>http://www.rockdai.com/?p=324</link>
		<comments>http://www.rockdai.com/?p=324#comments</comments>
		<pubDate>Sun, 23 May 2010 16:16:32 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[监控宝]]></category>
		<category><![CDATA[HTTP]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=324</guid>
		<description><![CDATA[Gmail 的产品经理Todd Jackson 在一次接受采访时谈到了 Gmail 的监控系统。那是一整面墙的仪表，通过它可以检测到整个系统的健康状况。负责站点的工程师们还佩戴了寻呼装置，哪怕系统只是出现了极小的问题，工程师们也会得到通知。 当然，以目前我们站点的规模来说，还用不上 Google 那么豪华的“监控墙”，但我们也丝毫不能在监控方面有所懈怠。重视监控，才能让我们及时了解到潜在的危险。 当我们要管理一个网站或者是一台服务器的时候，我们至少需要了解目前运行的站点是否可以通过正常的方式访问。试想一下，当用户正开始对你的网站逐渐报以好感的时候突然有一天让他怎么刷新也打不开想要的页面，虽然他还不至于因此而落泪但其沮丧之情是可想而知的。也许只是短短几个小时的宕机，都有可能在用户心中留下不好的印象。从此，给你网站今后用户量的流失埋下了伏笔。因此我们认为，无论您的站点目前正处于多大的规模，你都有必要为自己的网站部署一个最基本的可用性监控策略，并辅以有效的运行状况报告机制。只有这样，才能让你第一时间知晓网站目前的“健康状况”，让一切都在你的运筹帷幄之中。 那么，就让我们从这个最简单的需求开始，逐步深入地探究监控宝为我们提供的各项监控策略解决方案。 我们可以在监控宝中创建对URL的监控任务。进入管理界面（dashboard）之后可以在左侧边栏看到创建监控项目的选项。单击后可以看到监控宝为我们提供的4种监控项目类型：HTTP/HTTPs、 Ping、 FTP 以及 DNS。在接下来的篇幅中我们会分别对这几种监控类型作详细的介绍，以便让大家可以正确地判断自己应该使用何种服务。在那之前，先让我们来快速创建第一个HTTP监控项目吧。也许在之前你看过很多介绍编程语言的书籍，那么我们现在要创建的这个监控项目就可以被比喻为那个著名的“Hello, World!”程序——它虽然极其简单但却是我们长征路上的第一步，可谓意义非凡。 通过选择HTTP/HTTPs方式监控，监控宝将下载URL的响应内容并统计下载时间，从而获得该URL的可用率报告以及整体响应时间等信息。当然，我认为你应该对网站上的多个URL创建监控任务，这点非常重要，这可以让你了解到网站上多项服务的运行性能。如此以来，你可以从整体上对网站各模块的性能作横向的对比。另外，我还喜欢把竞争对手的部分URL也添加到我的监控列表里，让我可以真正做到知己知彼。 那么，我们应该对站点里哪些类型的URL添加监控才能通过它们获得对站点性能的有效分析呢？稍后我们会有专门的章节来讨论这个话题。 选择“网页和网页(HTTP/HTTPs)”类型后，进入下一步。接下来，我们需要填写监控目标的一些信息，其中监控项目名称和目标URL是必填内容，今后你一定会需要通过完善整个的表单来让监控宝为你提供更个性化的服务。但在这里我们先不考虑那些部分，因为我们现在需要快速地建立起第一个监控任务来看看监控宝是不是真的这么厉害。 作为示例，我们首先来监控豆瓣网的首页，填写如上图的信息。在页面的下端，我们可以根据自己的需要来选择监控频率和失败重试次数。 现在我把检查频率设置为默认的10分钟，这代表每10分钟监控宝就会对监控目标URL发起一起HTTP请求。如果没有特殊原因，我建议大家在平时的应用中也选择10分钟作为检查频率。因为更频繁的检查可以让我们更及时地发现站点的故障，从而更迅速地解决。并且我们还可以获得更准确的可用性报告和更丰富的性能分析数据。 同样的，我们还可以对站点内的其他URL进行监控，比如豆瓣小组首页。 OK，现在我们已经成功地创建了两个监控项目了。不出意外的话，大家应该可以看到如下图的提示信息。 点击“告警设置”可以配置监控项目告警的方式以及自定义告警线等。当然，稍后我们还可以更改设置，所以在这里我们先保持它为默认值，以后再进行详细的讨论。 细心的读者会发现豆瓣小组项目也被加入到了www.douban.com视图。监控宝会自动把同一域名下的监控项目归入同一个视图中，方便我们对其进行横向的管理。点击域名后就可以进入视图界面，但在这里我们暂时也不介绍它。 点击“详细情况”我们可以看到监控项目的报告，可以看到刚刚设置的豆瓣首页监控任务已经有了结果。也许看到这份报告的时候你的脸上已经情不自禁地露出了自豪的微笑——报告中看到的数字全部用绿色标记，这代表我们网站的访问速度很不错。 但监控宝给我们带来的惊喜才刚刚开始，通过查看详细的监控报告，我们还可以了解到监控项目的可用率统计、响应时间统计、响应时间分布等等。 现在，监控宝已经在日以继夜地为我们监控着我们的应用了。就像 Logo 上那只瞪大了眼睛的猫头鹰那样，监控宝会孜孜不倦地工作为我们送来监控任务的第一手运行状态报告。 如果你的需求仅仅是希望知道自己的站点是否可用的话，那么到目前为止监控宝的表现应该已经能够让你满意了。但我想大部分读者朋友都跟我一样，我们的好奇心绝不会仅仅局限于此。那么在下一节中，让我们来详细研究监控宝为我们生成的监控报告，一起讨论我们应该怎样分析报告里的数据从而帮助我们了解系统的瓶颈和潜在的危机。]]></description>
			<content:encoded><![CDATA[<p>Gmail 的产品经理Todd Jackson 在一次接受采访时谈到了 Gmail 的监控系统。那是一整面墙的仪表，通过它可以检测到整个系统的健康状况。负责站点的工程师们还佩戴了寻呼装置，哪怕系统只是出现了极小的问题，工程师们也会得到通知。</p>
<p>当然，以目前我们站点的规模来说，还用不上 Google 那么豪华的“监控墙”，但我们也丝毫不能在监控方面有所懈怠。重视监控，才能让我们及时了解到潜在的危险。</p>
<p>当我们要管理一个网站或者是一台服务器的时候，我们至少需要了解目前运行的站点是否可以通过正常的方式访问。试想一下，当用户正开始对你的网站逐渐报以好感的时候突然有一天让他怎么刷新也打不开想要的页面，虽然他还不至于因此而落泪但其沮丧之情是可想而知的。也许只是短短几个小时的宕机，都有可能在用户心中留下不好的印象。从此，给你网站今后用户量的流失埋下了伏笔。因此我们认为，无论您的站点目前正处于多大的规模，你都有必要为自己的网站部署一个最基本的可用性监控策略，并辅以有效的运行状况报告机制。只有这样，才能让你第一时间知晓网站目前的“健康状况”，让一切都在你的运筹帷幄之中。</p>
<p>那么，就让我们从这个最简单的需求开始，逐步深入地探究监控宝为我们提供的各项监控策略解决方案。<span id="more-324"></span></p>
<p><a href="http://www.rockdai.com/jiankongbao/1.jpg"><img class="alignnone" title="1" src="http://www.rockdai.com/jiankongbao/1.jpg" alt="" /></a></p>
<p>我们可以在监控宝中创建对URL的监控任务。进入管理界面（dashboard）之后可以在左侧边栏看到创建监控项目的选项。单击后可以看到监控宝为我们提供的4种监控项目类型：HTTP/HTTPs、 Ping、 FTP 以及 DNS。在接下来的篇幅中我们会分别对这几种监控类型作详细的介绍，以便让大家可以正确地判断自己应该使用何种服务。在那之前，先让我们来快速创建第一个HTTP监控项目吧。也许在之前你看过很多介绍编程语言的书籍，那么我们现在要创建的这个监控项目就可以被比喻为那个著名的“Hello, World!”程序——它虽然极其简单但却是我们长征路上的第一步，可谓意义非凡。</p>
<p><a href="http://www.rockdai.com/jiankongbao/2.jpg"><img class="alignnone" title="2" src="http://www.rockdai.com/jiankongbao/2.jpg" alt="" width="395" height="229" /></a></p>
<p>通过选择HTTP/HTTPs方式监控，监控宝将下载URL的响应内容并统计下载时间，从而获得该URL的可用率报告以及整体响应时间等信息。当然，我认为你应该对网站上的多个URL创建监控任务，这点非常重要，这可以让你了解到网站上多项服务的运行性能。如此以来，你可以从整体上对网站各模块的性能作横向的对比。另外，我还喜欢把竞争对手的部分URL也添加到我的监控列表里，让我可以真正做到知己知彼。</p>
<p>那么，我们应该对站点里哪些类型的URL添加监控才能通过它们获得对站点性能的有效分析呢？稍后我们会有专门的章节来讨论这个话题。</p>
<p>选择“网页和网页(HTTP/HTTPs)”类型后，进入下一步。接下来，我们需要填写监控目标的一些信息，其中监控项目名称和目标URL是必填内容，今后你一定会需要通过完善整个的表单来让监控宝为你提供更个性化的服务。但在这里我们先不考虑那些部分，因为我们现在需要快速地建立起第一个监控任务来看看监控宝是不是真的这么厉害。</p>
<p><a href="http://www.rockdai.com/jiankongbao/3.png"><img class="alignnone" title="3" src="http://www.rockdai.com/jiankongbao/3.png" alt="" width="442" height="162" /></a></p>
<p>作为示例，我们首先来监控豆瓣网的首页，填写如上图的信息。在页面的下端，我们可以根据自己的需要来选择监控频率和失败重试次数。</p>
<p><a href="http://www.rockdai.com/jiankongbao/4.png"><img class="alignnone" title="4" src="http://www.rockdai.com/jiankongbao/4.png" alt="" /></a></p>
<p>现在我把检查频率设置为默认的10分钟，这代表每10分钟监控宝就会对监控目标URL发起一起HTTP请求。如果没有特殊原因，我建议大家在平时的应用中也选择10分钟作为检查频率。因为更频繁的检查可以让我们更及时地发现站点的故障，从而更迅速地解决。并且我们还可以获得更准确的可用性报告和更丰富的性能分析数据。</p>
<p>同样的，我们还可以对站点内的其他URL进行监控，比如豆瓣小组首页。</p>
<p><a href="http://www.rockdai.com/jiankongbao/5.png"><img class="alignnone" title="5" src="http://www.rockdai.com/jiankongbao/5.png" alt="" width="439" height="162" /></a></p>
<p>OK，现在我们已经成功地创建了两个监控项目了。不出意外的话，大家应该可以看到如下图的提示信息。</p>
<p>点击“告警设置”可以配置监控项目告警的方式以及自定义告警线等。当然，稍后我们还可以更改设置，所以在这里我们先保持它为默认值，以后再进行详细的讨论。</p>
<p>细心的读者会发现豆瓣小组项目也被加入到了www.douban.com视图。监控宝会自动把同一域名下的监控项目归入同一个视图中，方便我们对其进行横向的管理。点击域名后就可以进入视图界面，但在这里我们暂时也不介绍它。</p>
<p><a href="http://www.rockdai.com/jiankongbao/6.png"><img class="alignnone" title="6" src="http://www.rockdai.com/jiankongbao/6.png" alt="" width="499" height="127" /></a></p>
<p>点击“详细情况”我们可以看到监控项目的报告，可以看到刚刚设置的豆瓣首页监控任务已经有了结果。也许看到这份报告的时候你的脸上已经情不自禁地露出了自豪的微笑——报告中看到的数字全部用绿色标记，这代表我们网站的访问速度很不错。</p>
<p>但监控宝给我们带来的惊喜才刚刚开始，通过查看详细的监控报告，我们还可以了解到监控项目的可用率统计、响应时间统计、响应时间分布等等。</p>
<p>现在，监控宝已经在日以继夜地为我们监控着我们的应用了。就像 Logo 上那只瞪大了眼睛的猫头鹰那样，监控宝会孜孜不倦地工作为我们送来监控任务的第一手运行状态报告。</p>
<p>如果你的需求仅仅是希望知道自己的站点是否可用的话，那么到目前为止监控宝的表现应该已经能够让你满意了。但我想大部分读者朋友都跟我一样，我们的好奇心绝不会仅仅局限于此。那么在下一节中，让我们来详细研究监控宝为我们生成的监控报告，一起讨论我们应该怎样分析报告里的数据从而帮助我们了解系统的瓶颈和潜在的危机。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=324</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从“失败鲸”到监控宝</title>
		<link>http://www.rockdai.com/?p=315</link>
		<comments>http://www.rockdai.com/?p=315#comments</comments>
		<pubDate>Sun, 23 May 2010 16:07:35 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[监控宝]]></category>
		<category><![CDATA[监控]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=315</guid>
		<description><![CDATA[每当 Twitter 因为服务器过载而宕掉的时候，我们就能看到这幅著名的，名为“失败鲸（Fail Whale）”的图片。Twitter 作为微博客这种新型社交模式的鼻祖，自2006年成立以来其用户量正在以每年1 382%的速度高速增长。然而在2007到2008年期间 Twitter 的多次大宕机，也一度让早期的用户们感到非常沮丧。在那一段时间里，和“失败鲸”相关的报道相继被美国各大知名媒体刊载，如此频繁的曝光让“失败鲸”成为了家喻户晓的明星，甚至还有属于自己的网站（http://www.failwhale.com/）。 当然，我们要讨论话题的关键并不是“失败鲸”。 我们经常在新闻中看到每当苹果公司推出新产品的时候，苹果的 Fans 们会在专卖店门前彻夜排队期待次日能第一时间体验到那宛如艺术般的产品。但我们很少看到有用户会对一个运行缓慢，甚至经常宕机的网站给予太多的宽容。 你也许已经习惯了在每次登陆自己运营的网站时那种行云流水般顺畅的感觉。因此，你也认为来自大江南北的所有用户都能像你一样获得如此愉悦的体验，同时你也许会相信即便你站点的访问量再剧增10倍，你的服务器依然可以为用户们提供健壮的服务。这些看起来都是事实，但是，我在此不得不善意地提醒你，也许你正在运营的站点不能像 Twitter 一样赢得如此多用户的青睐和谅解。所以一旦你的网站在某一天因为流量带来的压力过大开始出现访问速度下降，甚至偶尔给访客一张“404的臭脸”时，用户往往会毫不犹豫地选择转向其他的同类站点，更有甚者或许会到讨论区羞辱你的产品。与此同时你还得清楚，当你的竞争对手得知这一消息的时候，他们已经在筹划用户交接仪式了。 这听起来像是居安思危，当然我们也不得不警示自己，凡事只有部署好完备的解决方案才能做到临危不乱。我们只有做好充分的准备，才能阻止噩梦的发生。当我们知道发生了什么事情，才能明白如何解决。 我们应该让自己的服务器提供最高效的系统性能。富有经验的 webmasters 通常会为自己管理的服务器配置一套完备的监控策略，当服务器的性能突然低于它应有的水平的时候，问题可能来自正在执行的进程、内存的使用率、磁盘的性能、服务器I/O吞吐量、CPU的压力等等。在预算有限的今天，理解如何优化系统性能比以往任何时候都重要。要实现它的前提是，你必须充分了解自己的服务器的运行情况，从而找到真正的瓶颈所在。 我们接下来将要讨论到的内容将主要围绕着对站点和服务器的监控策略展开。我们首先要讨论一些普遍的方法和基础的工具，来帮助读者对如何处理基本的服务器性能问题有个基本的了解。然后我们要着重介绍一个更高效的解决方案——通过使用监控宝（www.jiankongbao.com）来为站点部署更简单但实用的监控策略。 在敏捷思想盛行的今天，对于大部分的中小型网站来说自建一套完备的监控系统无论从人力或财力上来说都不会是科学的策略。而监控宝则为我们提供了一个降低监控成本的最佳选择。 监控宝以云服务的方式，为各种部署形式的网站提供服务，无论你使用的是虚拟主机、托管主机还是虚拟化主机(VPS)都能体验到监控宝强大的功能。监控宝使用包括HTTP、Ping、DNS、FTP、SMTP、POP、IMAP、TCP等在内的各种网络协议进行站点监控，确保符合用户更多的个性化需求。此外我们还可以使用 SNMP 协议监控服务器性能和容量，支持各种服务器包括Linux、Windows、BSD、Mac等。更让人欣喜的是，现在监控宝已支持服务层监控，可监控的对象包括Apache、Lighttpd、Nginx、MySQL。如此丰富的功能，可以帮助我们更好地提升站点可用性，并对设备的运行状况进行监控。即便是大型网站，也可以利用监控宝来作为自身监控系统之外的有效补充。除此之外监控宝简单而友好的界面可以让稍欠经验的用户也能快速上手，清晰易读的监控报告则让我们得以从纷繁复杂的数据报告中抽身而出。 接下来的几天，让我们一起来讨论如何从基础应用开始，一步一步使用监控宝打造我们的服务器监控解决方案。]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.rockdai.com/jiankongbao/FailWhale.gif"><img class="alignnone" title="失败鲸" src="http://www.rockdai.com/jiankongbao/FailWhale.gif" alt="失败鲸" width="461" height="249" /></a></p>
<p>每当 Twitter 因为服务器过载而宕掉的时候，我们就能看到这幅著名的，名为“失败鲸（Fail Whale）”的图片。Twitter 作为微博客这种新型社交模式的鼻祖，自2006年成立以来其用户量正在以每年1 382%的速度高速增长。然而在2007到2008年期间 Twitter 的多次大宕机，也一度让早期的用户们感到非常沮丧。在那一段时间里，和“失败鲸”相关的报道相继被美国各大知名媒体刊载，如此频繁的曝光让“失败鲸”成为了家喻户晓的明星，甚至还有属于自己的网站（<a href="http://www.failwhale.com/">http://www.failwhale.com/</a>）。</p>
<p>当然，我们要讨论话题的关键并不是“失败鲸”。</p>
<p>我们经常在新闻中看到每当苹果公司推出新产品的时候，苹果的 Fans 们会在专卖店门前彻夜排队期待次日能第一时间体验到那宛如艺术般的产品。但我们很少看到有用户会对一个运行缓慢，甚至经常宕机的网站给予太多的宽容。</p>
<p>你也许已经习惯了在每次登陆自己运营的网站时那种行云流水般顺畅的感觉。因此，你也认为来自大江南北的所有用户都能像你一样获得如此愉悦的体验，同时你也许会相信即便你站点的访问量再剧增10倍，你的服务器依然可以为用户们提供健壮的服务。这些看起来都是事实，但是，我在此不得不善意地提醒你，也许你正在运营的站点不能像 Twitter 一样赢得如此多用户的青睐和谅解。所以一旦你的网站在某一天因为流量带来的压力过大开始出现访问速度下降，甚至偶尔给访客一张“404的臭脸”时，用户往往会毫不犹豫地选择转向其他的同类站点，更有甚者或许会到讨论区羞辱你的产品。与此同时你还得清楚，当你的竞争对手得知这一消息的时候，他们已经在筹划用户交接仪式了。</p>
<p>这听起来像是居安思危，当然我们也不得不警示自己，凡事只有部署好完备的解决方案才能做到临危不乱。我们只有做好充分的准备，才能阻止噩梦的发生。当我们知道发生了什么事情，才能明白如何解决。</p>
<p>我们应该让自己的服务器提供最高效的系统性能。富有经验的 webmasters 通常会为自己管理的服务器配置一套完备的监控策略，当服务器的性能突然低于它应有的水平的时候，问题可能来自正在执行的进程、内存的使用率、磁盘的性能、服务器I/O吞吐量、CPU的压力等等。在预算有限的今天，理解如何优化系统性能比以往任何时候都重要。要实现它的前提是，你必须充分了解自己的服务器的运行情况，从而找到真正的瓶颈所在。</p>
<p><a href="http://www.rockdai.com/jiankongbao/JianKongBao.gif"><img class="alignnone" title="监控宝" src="http://www.rockdai.com/jiankongbao/JianKongBao.gif" alt="监控宝" /></a></p>
<p>我们接下来将要讨论到的内容将主要围绕着对站点和服务器的监控策略展开。我们首先要讨论一些普遍的方法和基础的工具，来帮助读者对如何处理基本的服务器性能问题有个基本的了解。然后我们要着重介绍一个更高效的解决方案——通过使用监控宝（www.jiankongbao.com）来为站点部署更简单但实用的监控策略。</p>
<p>在敏捷思想盛行的今天，对于大部分的中小型网站来说自建一套完备的监控系统无论从人力或财力上来说都不会是科学的策略。而监控宝则为我们提供了一个降低监控成本的最佳选择。</p>
<p>监控宝以云服务的方式，为各种部署形式的网站提供服务，无论你使用的是虚拟主机、托管主机还是虚拟化主机(VPS)都能体验到监控宝强大的功能。监控宝使用包括HTTP、Ping、DNS、FTP、SMTP、POP、IMAP、TCP等在内的各种网络协议进行站点监控，确保符合用户更多的个性化需求。此外我们还可以使用 SNMP 协议监控服务器性能和容量，支持各种服务器包括Linux、Windows、BSD、Mac等。更让人欣喜的是，现在监控宝已支持服务层监控，可监控的对象包括Apache、Lighttpd、Nginx、MySQL。如此丰富的功能，可以帮助我们更好地提升站点可用性，并对设备的运行状况进行监控。即便是大型网站，也可以利用监控宝来作为自身监控系统之外的有效补充。除此之外监控宝简单而友好的界面可以让稍欠经验的用户也能快速上手，清晰易读的监控报告则让我们得以从纷繁复杂的数据报告中抽身而出。</p>
<p>接下来的几天，让我们一起来讨论如何从基础应用开始，一步一步使用监控宝打造我们的服务器监控解决方案。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=315</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>解决Visual C++ 编译器中混合 .c 文件时收到 C1853 预编译头错误的方法</title>
		<link>http://www.rockdai.com/?p=310</link>
		<comments>http://www.rockdai.com/?p=310#comments</comments>
		<pubDate>Thu, 18 Mar 2010 15:18:54 +0000</pubDate>
		<dc:creator>小戴</dc:creator>
				<category><![CDATA[学习]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[C1853]]></category>
		<category><![CDATA[Visual C++]]></category>
		<category><![CDATA[错误]]></category>

		<guid isPermaLink="false">http://www.rockdai.com/?p=310</guid>
		<description><![CDATA[当 Visual C++ 项目启用了预编译头 (Precompiled header) 功能时，如果项目中同时混合有 .c 和 .cpp 源文件，则可能收到 C1853 编译器错误：fatal error C1853: &#8216;pjtname.pch&#8217; precompiled header file is from a previous version of the compiler, or the precompiled header is C++ and you are using it from C &#8230; <a href="http://www.rockdai.com/?p=310">继续阅读 <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>当 Visual C++ 项目启用了预编译头 (Precompiled header) 功能时，如果项目中同时混合有 .c 和 .cpp 源文件，则可能收到 C1853 编译器错误：fatal error C1853: &#8216;pjtname.pch&#8217; precompiled header file is from a previous version of the compiler, or the precompiled header is C++ and you are using it from C (or vice versa)（致命错误C1853:  “filename.pch”预编译头文件来自编译器的早期版本，或者预编译头为C++ 而在C 中使用它(或相反)）。</p>
<p>该错误是因为当项目中混合了 .cpp 和 .c 文件时，编译器会对它们采取不同的编译方式（主要是因为对函数声明的处理方式不同），因而不能共用一个预编译头文件。在 VC++ 中，默认的预编译头文件是针对 C++ 的 (stdafx.h 和 stdafx.cpp），当然也可以创建针对 C 的预编译头。有趣的是，在旧版的 VC++ 中，这个错误的提示很具有误导性：fatal error C1853: &#8216;xxx.pch&#8217; is not a precompiled header file created with this compiler. 常常让人摸不着头脑。应该说，在新版中的这个提示是有所改进的。不过在网上搜索一番，对这个问题往往都是建议对整个项目取消预编译头的设置。这显然不是一个好的解决方案。对于一个比较大的工程来说，使用预编译头可以使总的编译时间大大减少。因而保留预编译头的设置才是比较好的解决方案。搜索 MSDN，针对不同的情况，可以有不同的解决方案：</p>
<p><span id="more-310"></span></p>
<p>方案1：适用于绝大多数文件是 .cpp 或绝大多数文件是.c的情况。在这种情况下，将少数的不同类文件设为不使用预编译头是比较平衡的做法，方法是：对于 VC++6.0，在 FileView 里对要取消预编译头的 .c (或 .cpp) 文件点右键，选择 settings，在弹出的对话框右边选择 category 为 precompiled headers，再设置选项为 not using &#8230;；对于 VS2005，则在 solution explorer 中对相应文件点右键选择 properties，在 precompiled headers 项下设置 not using&#8230; 即可。如果需要设置多个文件，则可以按住 Ctrl 键再同时选中这些文件并设置。</p>
<p>方案2：如果受影响的文件比较多，则把它们都设置禁止预编译头的话仍然会使项目总体的编译速度大大降低，得不偿失。这时考虑可以为这组文件建立专用的预编译头。在 VC++ 极早期版本（1.5及以前版本）中是支持单个工程中建立分别针对 .c 和 .cpp 的预编译头的，但之后的版本中只支持单独的预编译头。在这种情况下，我们可以在workspace（或 solution）中建立一个新的静态链接库 (Static Library) 工程，将所有的 .c 文件独立出来加入到该工程中单独编译，这样就可以在该静态链接库中针对 .c 文件创建预编译头。但是这样做在一定程度上需要被独立出来的代码在逻辑上是属于同一模块中的，这样才便于维护。不过从设计的角度来说，这个要求一般是满足的，否则就应考虑下项目的总体设计了:P 最后别忘了设置原项目的依赖项 (dependency) 为独立出来的这个静态库项目。</p>
<p>转自 <a href="http://live.aulddays.com/tech/08/c1853precompile/">http://live.aulddays.com/tech/08/c1853precompile/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.rockdai.com/?feed=rss2&amp;p=310</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
